aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPieter Wuille <pieter.wuille@gmail.com>2015-07-28 20:11:20 +0200
committerPieter Wuille <pieter.wuille@gmail.com>2015-11-15 16:06:57 +0100
commit6e182686163ce3c15b878bd78c41d8d18db344f1 (patch)
treed4e0997b1459def528557d640a480937ffc207fd
parentb632145edeb376b4d1597f192ca00634f7d2866c (diff)
downloadbitcoin-6e182686163ce3c15b878bd78c41d8d18db344f1.tar.xz
Switch to libsecp256k1-based validation for ECDSA
-rw-r--r--doc/release-notes.md15
-rw-r--r--src/Makefile.am10
-rw-r--r--src/bitcoin-tx.cpp10
-rw-r--r--src/eccryptoverify.cpp68
-rw-r--r--src/eccryptoverify.h21
-rw-r--r--src/ecwrapper.cpp218
-rw-r--r--src/ecwrapper.h40
-rw-r--r--src/init.cpp6
-rw-r--r--src/pubkey.cpp260
-rw-r--r--src/pubkey.h16
-rw-r--r--src/script/bitcoinconsensus.cpp9
-rw-r--r--src/script/interpreter.cpp13
-rw-r--r--src/test/test_bitcoin.h3
13 files changed, 291 insertions, 398 deletions
diff --git a/doc/release-notes.md b/doc/release-notes.md
index e4dcc60cff..7db27f9fac 100644
--- a/doc/release-notes.md
+++ b/doc/release-notes.md
@@ -190,6 +190,21 @@ calculating the target.
A more detailed documentation about keeping traffic low can be found in
[/doc/reducetraffic.md](/doc/reducetraffic.md).
+Signature validation using libsecp256k1
+---------------------------------------
+
+ECDSA signatures inside Bitcoin transactions now use validation using
+[https://github.com/bitcoin/secp256k1](libsecp256k1) instead of OpenSSL.
+
+Depending on the platform, this means a significant speedup for raw signature
+validation speed. The advantage is largest on x86_64, where validation is over
+five times faster. In practice, this translates to a raw reindexing and new
+block validation times that are less than half of what it was before.
+
+Libsecp256k1 has undergone very extensive testing and validation.
+
+A side effect of this change is that libconsensus no longer depends on OpenSSL.
+
0.12.0 Change log
=================
diff --git a/src/Makefile.am b/src/Makefile.am
index 834c3dc891..f1e98dabde 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -104,8 +104,6 @@ BITCOIN_CORE_H = \
consensus/validation.h \
core_io.h \
core_memusage.h \
- eccryptoverify.h \
- ecwrapper.h \
hash.h \
httprpc.h \
httpserver.h \
@@ -272,8 +270,6 @@ libbitcoin_common_a_SOURCES = \
compressor.cpp \
core_read.cpp \
core_write.cpp \
- eccryptoverify.cpp \
- ecwrapper.cpp \
hash.cpp \
key.cpp \
keystore.cpp \
@@ -404,8 +400,6 @@ libbitcoinconsensus_la_SOURCES = \
crypto/sha1.cpp \
crypto/sha256.cpp \
crypto/sha512.cpp \
- eccryptoverify.cpp \
- ecwrapper.cpp \
hash.cpp \
primitives/transaction.cpp \
pubkey.cpp \
@@ -420,8 +414,8 @@ if GLIBC_BACK_COMPAT
endif
libbitcoinconsensus_la_LDFLAGS = $(AM_LDFLAGS) -no-undefined $(RELDFLAGS)
-libbitcoinconsensus_la_LIBADD = $(CRYPTO_LIBS)
-libbitcoinconsensus_la_CPPFLAGS = $(AM_CPPFLAGS) $(CRYPTO_CFLAGS) -I$(builddir)/obj -DBUILD_BITCOIN_INTERNAL
+libbitcoinconsensus_la_LIBADD = $(LIBSECP256K1)
+libbitcoinconsensus_la_CPPFLAGS = $(AM_CPPFLAGS) -I$(builddir)/obj -I$(srcdir)/secp256k1/include -DBUILD_BITCOIN_INTERNAL
libbitcoinconsensus_la_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
endif
diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp
index 48033cd8ad..9f8b2b98af 100644
--- a/src/bitcoin-tx.cpp
+++ b/src/bitcoin-tx.cpp
@@ -477,9 +477,15 @@ static void MutateTxSign(CMutableTransaction& tx, const string& flagStr)
class Secp256k1Init
{
+ ECCVerifyHandle globalVerifyHandle;
+
public:
- Secp256k1Init() { ECC_Start(); }
- ~Secp256k1Init() { ECC_Stop(); }
+ Secp256k1Init() {
+ ECC_Start();
+ }
+ ~Secp256k1Init() {
+ ECC_Stop();
+ }
};
static void MutateTx(CMutableTransaction& tx, const string& command,
diff --git a/src/eccryptoverify.cpp b/src/eccryptoverify.cpp
deleted file mode 100644
index e894e1122c..0000000000
--- a/src/eccryptoverify.cpp
+++ /dev/null
@@ -1,68 +0,0 @@
-// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2014 The Bitcoin Core developers
-// Distributed under the MIT software license, see the accompanying
-// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-
-#include "eccryptoverify.h"
-
-namespace {
-
-int CompareBigEndian(const unsigned char *c1, size_t c1len, const unsigned char *c2, size_t c2len) {
- while (c1len > c2len) {
- if (*c1)
- return 1;
- c1++;
- c1len--;
- }
- while (c2len > c1len) {
- if (*c2)
- return -1;
- c2++;
- c2len--;
- }
- while (c1len > 0) {
- if (*c1 > *c2)
- return 1;
- if (*c2 > *c1)
- return -1;
- c1++;
- c2++;
- c1len--;
- }
- return 0;
-}
-
-/** Order of secp256k1's generator minus 1. */
-const unsigned char vchMaxModOrder[32] = {
- 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
- 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,
- 0xBA,0xAE,0xDC,0xE6,0xAF,0x48,0xA0,0x3B,
- 0xBF,0xD2,0x5E,0x8C,0xD0,0x36,0x41,0x40
-};
-
-/** Half of the order of secp256k1's generator minus 1. */
-const unsigned char vchMaxModHalfOrder[32] = {
- 0x7F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
- 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
- 0x5D,0x57,0x6E,0x73,0x57,0xA4,0x50,0x1D,
- 0xDF,0xE9,0x2F,0x46,0x68,0x1B,0x20,0xA0
-};
-
-const unsigned char vchZero[1] = {0};
-} // anon namespace
-
-namespace eccrypto {
-
-bool Check(const unsigned char *vch) {
- return vch &&
- CompareBigEndian(vch, 32, vchZero, 0) > 0 &&
- CompareBigEndian(vch, 32, vchMaxModOrder, 32) <= 0;
-}
-
-bool CheckSignatureElement(const unsigned char *vch, int len, bool half) {
- return vch &&
- CompareBigEndian(vch, len, vchZero, 0) > 0 &&
- CompareBigEndian(vch, len, half ? vchMaxModHalfOrder : vchMaxModOrder, 32) <= 0;
-}
-
-} // namespace eccrypto
diff --git a/src/eccryptoverify.h b/src/eccryptoverify.h
deleted file mode 100644
index c67c1e44fc..0000000000
--- a/src/eccryptoverify.h
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2014 The Bitcoin Core developers
-// Distributed under the MIT software license, see the accompanying
-// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-
-#ifndef BITCOIN_ECCRYPTOVERIFY_H
-#define BITCOIN_ECCRYPTOVERIFY_H
-
-#include <vector>
-#include <cstdlib>
-
-class uint256;
-
-namespace eccrypto {
-
-bool Check(const unsigned char *vch);
-bool CheckSignatureElement(const unsigned char *vch, int len, bool half);
-
-} // eccrypto namespace
-
-#endif // BITCOIN_ECCRYPTOVERIFY_H
diff --git a/src/ecwrapper.cpp b/src/ecwrapper.cpp
deleted file mode 100644
index f94bc954fd..0000000000
--- a/src/ecwrapper.cpp
+++ /dev/null
@@ -1,218 +0,0 @@
-// Copyright (c) 2009-2014 The Bitcoin Core 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 <openssl/bn.h>
-#include <openssl/ecdsa.h>
-#include <openssl/obj_mac.h>
-
-namespace {
-
-class ecgroup_order
-{
-public:
- static const EC_GROUP* get()
- {
- static const ecgroup_order wrapper;
- return wrapper.pgroup;
- }
-
-private:
- ecgroup_order()
- : pgroup(EC_GROUP_new_by_curve_name(NID_secp256k1))
- {
- }
-
- ~ecgroup_order()
- {
- EC_GROUP_free(pgroup);
- }
-
- EC_GROUP* pgroup;
-};
-
-/**
- * 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();
- assert(pkey != NULL);
- int result = EC_KEY_set_group(pkey, ecgroup_order::get());
- assert(result);
-}
-
-CECKey::~CECKey() {
- EC_KEY_free(pkey);
-}
-
-void CECKey::GetPubKey(std::vector<unsigned char> &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<unsigned char>& 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()
-{
- const EC_GROUP *pgroup = ecgroup_order::get();
- if(pgroup == NULL)
- return false;
- // 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 efb6cd18a7..0000000000
--- a/src/ecwrapper.h
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright (c) 2009-2014 The Bitcoin Core 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 <cstddef>
-#include <vector>
-
-#include <openssl/ec.h>
-
-class uint256;
-
-/** RAII Wrapper around OpenSSL's EC_KEY */
-class CECKey {
-private:
- EC_KEY *pkey;
-
-public:
- CECKey();
- ~CECKey();
-
- void GetPubKey(std::vector<unsigned char>& pubkey, bool fCompressed);
- bool SetPubKey(const unsigned char* pubkey, size_t size);
- bool Verify(const uint256 &hash, const std::vector<unsigned char>& 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 36c1dfbe86..d768c4837e 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -154,6 +154,7 @@ public:
static CCoinsViewDB *pcoinsdbview = NULL;
static CCoinsViewErrorCatcher *pcoinscatcher = NULL;
+static boost::scoped_ptr<ECCVerifyHandle> globalVerifyHandle;
void Interrupt(boost::thread_group& threadGroup)
{
@@ -243,6 +244,7 @@ void Shutdown()
delete pwalletMain;
pwalletMain = NULL;
#endif
+ globalVerifyHandle.reset();
ECC_Stop();
LogPrintf("%s: done\n", __func__);
}
@@ -649,8 +651,7 @@ void ThreadImport(std::vector<boost::filesystem::path> vImportFiles)
bool InitSanityCheck(void)
{
if(!ECC_InitSanityCheck()) {
- InitError("OpenSSL appears to lack support for elliptic curve cryptography. For more "
- "information, visit https://en.bitcoin.it/wiki/OpenSSL_and_EC_Libraries");
+ InitError("Elliptic curve cryptography sanity check failure. Aborting.");
return false;
}
if (!glibc_sanity_test() || !glibcxx_sanity_test())
@@ -991,6 +992,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
// Initialize elliptic curve code
ECC_Start();
+ globalVerifyHandle.reset(new ECCVerifyHandle());
// Sanity check
if (!InitSanityCheck())
diff --git a/src/pubkey.cpp b/src/pubkey.cpp
index bdab137600..6ebb152c75 100644
--- a/src/pubkey.cpp
+++ b/src/pubkey.cpp
@@ -4,19 +4,184 @@
#include "pubkey.h"
-#include "eccryptoverify.h"
+#include <secp256k1.h>
+#include <secp256k1_recovery.h>
-#include "ecwrapper.h"
+namespace
+{
+/* Global secp256k1_context object used for verification. */
+secp256k1_context* secp256k1_context_verify = NULL;
+}
+
+/** This function is taken from the libsecp256k1 distribution and implements
+ * DER parsing for ECDSA signatures, while supporting an arbitrary subset of
+ * format violations.
+ *
+ * Supported violations include negative integers, excessive padding, garbage
+ * at the end, and overly long length descriptors. This is safe to use in
+ * Bitcoin because since the activation of BIP66, signatures are verified to be
+ * strict DER before being passed to this module, and we know it supports all
+ * violations present in the blockchain before that point.
+ */
+static int ecdsa_signature_parse_der_lax(const secp256k1_context* ctx, secp256k1_ecdsa_signature* sig, const unsigned char *input, size_t inputlen) {
+ size_t rpos, rlen, spos, slen;
+ size_t pos = 0;
+ size_t lenbyte;
+ unsigned char tmpsig[64] = {0};
+ int overflow = 0;
+
+ /* Hack to initialize sig with a correctly-parsed but invalid signature. */
+ secp256k1_ecdsa_signature_parse_compact(ctx, sig, tmpsig);
+
+ /* Sequence tag byte */
+ if (pos == inputlen || input[pos] != 0x30) {
+ return 0;
+ }
+ pos++;
+
+ /* Sequence length bytes */
+ if (pos == inputlen) {
+ return 0;
+ }
+ lenbyte = input[pos++];
+ if (lenbyte & 0x80) {
+ lenbyte -= 0x80;
+ if (pos + lenbyte > inputlen) {
+ return 0;
+ }
+ pos += lenbyte;
+ }
+
+ /* Integer tag byte for R */
+ if (pos == inputlen || input[pos] != 0x02) {
+ return 0;
+ }
+ pos++;
+
+ /* Integer length for R */
+ if (pos == inputlen) {
+ return 0;
+ }
+ lenbyte = input[pos++];
+ if (lenbyte & 0x80) {
+ lenbyte -= 0x80;
+ if (pos + lenbyte > inputlen) {
+ return 0;
+ }
+ while (lenbyte > 0 && input[pos] == 0) {
+ pos++;
+ lenbyte--;
+ }
+ if (lenbyte >= sizeof(size_t)) {
+ return 0;
+ }
+ rlen = 0;
+ while (lenbyte > 0) {
+ rlen = (rlen << 8) + input[pos];
+ pos++;
+ lenbyte--;
+ }
+ } else {
+ rlen = lenbyte;
+ }
+ if (rlen > inputlen - pos) {
+ return 0;
+ }
+ rpos = pos;
+ pos += rlen;
+
+ /* Integer tag byte for S */
+ if (pos == inputlen || input[pos] != 0x02) {
+ return 0;
+ }
+ pos++;
+
+ /* Integer length for S */
+ if (pos == inputlen) {
+ return 0;
+ }
+ lenbyte = input[pos++];
+ if (lenbyte & 0x80) {
+ lenbyte -= 0x80;
+ if (pos + lenbyte > inputlen) {
+ return 0;
+ }
+ while (lenbyte > 0 && input[pos] == 0) {
+ pos++;
+ lenbyte--;
+ }
+ if (lenbyte >= sizeof(size_t)) {
+ return 0;
+ }
+ slen = 0;
+ while (lenbyte > 0) {
+ slen = (slen << 8) + input[pos];
+ pos++;
+ lenbyte--;
+ }
+ } else {
+ slen = lenbyte;
+ }
+ if (slen > inputlen - pos) {
+ return 0;
+ }
+ spos = pos;
+ pos += slen;
+
+ /* Ignore leading zeroes in R */
+ while (rlen > 0 && input[rpos] == 0) {
+ rlen--;
+ rpos++;
+ }
+ /* Copy R value */
+ if (rlen > 32) {
+ overflow = 1;
+ } else {
+ memcpy(tmpsig + 32 - rlen, input + rpos, rlen);
+ }
+
+ /* Ignore leading zeroes in S */
+ while (slen > 0 && input[spos] == 0) {
+ slen--;
+ spos++;
+ }
+ /* Copy S value */
+ if (slen > 32) {
+ overflow = 1;
+ } else {
+ memcpy(tmpsig + 64 - slen, input + spos, slen);
+ }
+
+ if (!overflow) {
+ overflow = !secp256k1_ecdsa_signature_parse_compact(ctx, sig, tmpsig);
+ }
+ if (overflow) {
+ /* Overwrite the result again with a correctly-parsed but invalid
+ signature if parsing failed. */
+ memset(tmpsig, 0, 64);
+ secp256k1_ecdsa_signature_parse_compact(ctx, sig, tmpsig);
+ }
+ return 1;
+}
bool CPubKey::Verify(const uint256 &hash, const std::vector<unsigned char>& vchSig) const {
if (!IsValid())
return false;
- CECKey key;
- if (!key.SetPubKey(begin(), size()))
+ secp256k1_pubkey pubkey;
+ secp256k1_ecdsa_signature sig;
+ if (!secp256k1_ec_pubkey_parse(secp256k1_context_verify, &pubkey, &(*this)[0], size())) {
return false;
- if (!key.Verify(hash, vchSig))
+ }
+ if (vchSig.size() == 0) {
return false;
- return true;
+ }
+ if (!ecdsa_signature_parse_der_lax(secp256k1_context_verify, &sig, &vchSig[0], vchSig.size())) {
+ return false;
+ }
+ /* libsecp256k1's ECDSA verification requires lower-S signatures, which have
+ * not historically been enforced in Bitcoin, so normalize them first. */
+ secp256k1_ecdsa_signature_normalize(secp256k1_context_verify, &sig, &sig);
+ return secp256k1_ecdsa_verify(secp256k1_context_verify, &sig, hash.begin(), &pubkey);
}
bool CPubKey::RecoverCompact(const uint256 &hash, const std::vector<unsigned char>& vchSig) {
@@ -24,33 +189,39 @@ bool CPubKey::RecoverCompact(const uint256 &hash, const std::vector<unsigned cha
return false;
int recid = (vchSig[0] - 27) & 3;
bool fComp = ((vchSig[0] - 27) & 4) != 0;
- CECKey key;
- if (!key.Recover(hash, &vchSig[1], recid))
+ secp256k1_pubkey pubkey;
+ secp256k1_ecdsa_recoverable_signature sig;
+ if (!secp256k1_ecdsa_recoverable_signature_parse_compact(secp256k1_context_verify, &sig, &vchSig[1], recid)) {
return false;
- std::vector<unsigned char> pubkey;
- key.GetPubKey(pubkey, fComp);
- Set(pubkey.begin(), pubkey.end());
+ }
+ if (!secp256k1_ecdsa_recover(secp256k1_context_verify, &pubkey, &sig, hash.begin())) {
+ return false;
+ }
+ unsigned char pub[65];
+ size_t publen = 65;
+ secp256k1_ec_pubkey_serialize(secp256k1_context_verify, pub, &publen, &pubkey, fComp ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED);
+ Set(pub, pub + publen);
return true;
}
bool CPubKey::IsFullyValid() const {
if (!IsValid())
return false;
- CECKey key;
- if (!key.SetPubKey(begin(), size()))
- return false;
- return true;
+ secp256k1_pubkey pubkey;
+ return secp256k1_ec_pubkey_parse(secp256k1_context_verify, &pubkey, &(*this)[0], size());
}
bool CPubKey::Decompress() {
if (!IsValid())
return false;
- CECKey key;
- if (!key.SetPubKey(begin(), size()))
+ secp256k1_pubkey pubkey;
+ if (!secp256k1_ec_pubkey_parse(secp256k1_context_verify, &pubkey, &(*this)[0], size())) {
return false;
- std::vector<unsigned char> pubkey;
- key.GetPubKey(pubkey, false);
- Set(pubkey.begin(), pubkey.end());
+ }
+ unsigned char pub[65];
+ size_t publen = 65;
+ secp256k1_ec_pubkey_serialize(secp256k1_context_verify, pub, &publen, &pubkey, SECP256K1_EC_UNCOMPRESSED);
+ Set(pub, pub + publen);
return true;
}
@@ -61,13 +232,18 @@ bool CPubKey::Derive(CPubKey& pubkeyChild, ChainCode &ccChild, unsigned int nChi
unsigned char out[64];
BIP32Hash(cc, nChild, *begin(), begin()+1, out);
memcpy(ccChild.begin(), out+32, 32);
- CECKey key;
- bool ret = key.SetPubKey(begin(), size());
- ret &= key.TweakPublic(out);
- std::vector<unsigned char> pubkey;
- key.GetPubKey(pubkey, true);
- pubkeyChild.Set(pubkey.begin(), pubkey.end());
- return ret;
+ secp256k1_pubkey pubkey;
+ if (!secp256k1_ec_pubkey_parse(secp256k1_context_verify, &pubkey, &(*this)[0], size())) {
+ return false;
+ }
+ if (!secp256k1_ec_pubkey_tweak_add(secp256k1_context_verify, &pubkey, out)) {
+ return false;
+ }
+ unsigned char pub[33];
+ size_t publen = 33;
+ secp256k1_ec_pubkey_serialize(secp256k1_context_verify, pub, &publen, &pubkey, SECP256K1_EC_COMPRESSED);
+ pubkeyChild.Set(pub, pub + publen);
+ return true;
}
void CExtPubKey::Encode(unsigned char code[74]) const {
@@ -95,3 +271,33 @@ bool CExtPubKey::Derive(CExtPubKey &out, unsigned int nChild) const {
out.nChild = nChild;
return pubkey.Derive(out.pubkey, out.chaincode, nChild, chaincode);
}
+
+/* static */ bool CPubKey::CheckLowS(const std::vector<unsigned char>& vchSig) {
+ secp256k1_ecdsa_signature sig;
+ if (!ecdsa_signature_parse_der_lax(secp256k1_context_verify, &sig, &vchSig[0], vchSig.size())) {
+ return false;
+ }
+ return (!secp256k1_ecdsa_signature_normalize(secp256k1_context_verify, NULL, &sig));
+}
+
+/* static */ int ECCVerifyHandle::refcount = 0;
+
+ECCVerifyHandle::ECCVerifyHandle()
+{
+ if (refcount == 0) {
+ assert(secp256k1_context_verify == NULL);
+ secp256k1_context_verify = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY);
+ assert(secp256k1_context_verify != NULL);
+ }
+ refcount++;
+}
+
+ECCVerifyHandle::~ECCVerifyHandle()
+{
+ refcount--;
+ if (refcount == 0) {
+ assert(secp256k1_context_verify != NULL);
+ secp256k1_context_destroy(secp256k1_context_verify);
+ secp256k1_context_verify = NULL;
+ }
+}
diff --git a/src/pubkey.h b/src/pubkey.h
index cce9c826e5..a1d437e706 100644
--- a/src/pubkey.h
+++ b/src/pubkey.h
@@ -177,6 +177,11 @@ public:
*/
bool Verify(const uint256& hash, const std::vector<unsigned char>& vchSig) const;
+ /**
+ * Check whether a signature is normalized (lower-S).
+ */
+ static bool CheckLowS(const std::vector<unsigned char>& vchSig);
+
//! Recover a public key from a compact signature.
bool RecoverCompact(const uint256& hash, const std::vector<unsigned char>& vchSig);
@@ -205,4 +210,15 @@ struct CExtPubKey {
bool Derive(CExtPubKey& out, unsigned int nChild) const;
};
+/** Users of this module must hold an ECCVerifyHandle. The constructor and
+ * destructor of these are not allowed to run in parallel, though. */
+class ECCVerifyHandle
+{
+ static int refcount;
+
+public:
+ ECCVerifyHandle();
+ ~ECCVerifyHandle();
+};
+
#endif // BITCOIN_PUBKEY_H
diff --git a/src/script/bitcoinconsensus.cpp b/src/script/bitcoinconsensus.cpp
index b0d5faaf77..79504f6ad3 100644
--- a/src/script/bitcoinconsensus.cpp
+++ b/src/script/bitcoinconsensus.cpp
@@ -6,6 +6,7 @@
#include "bitcoinconsensus.h"
#include "primitives/transaction.h"
+#include "pubkey.h"
#include "script/interpreter.h"
#include "version.h"
@@ -60,7 +61,13 @@ inline int set_error(bitcoinconsensus_error* ret, bitcoinconsensus_error serror)
return 0;
}
-} // anon namespace
+struct ECCryptoClosure
+{
+ ECCVerifyHandle handle;
+};
+
+ECCryptoClosure instance_of_eccryptoclosure;
+}
int bitcoinconsensus_verify_script(const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen,
const unsigned char *txTo , unsigned int txToLen,
diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp
index 6a20d497c0..8dcab832cb 100644
--- a/src/script/interpreter.cpp
+++ b/src/script/interpreter.cpp
@@ -9,7 +9,6 @@
#include "crypto/ripemd160.h"
#include "crypto/sha1.h"
#include "crypto/sha256.h"
-#include "eccryptoverify.h"
#include "pubkey.h"
#include "script/script.h"
#include "uint256.h"
@@ -165,16 +164,8 @@ bool static IsLowDERSignature(const valtype &vchSig, ScriptError* serror) {
if (!IsValidSignatureEncoding(vchSig)) {
return set_error(serror, SCRIPT_ERR_SIG_DER);
}
- unsigned int nLenR = vchSig[3];
- unsigned int nLenS = vchSig[5+nLenR];
- const unsigned char *S = &vchSig[6+nLenR];
- // If the S value is above the order of the curve divided by two, its
- // complement modulo the order could have been used instead, which is
- // one byte shorter when encoded correctly.
- if (!eccrypto::CheckSignatureElement(S, nLenS, true))
- return set_error(serror, SCRIPT_ERR_SIG_HIGH_S);
-
- return true;
+ std::vector<unsigned char> vchSigCopy(vchSig.begin(), vchSig.begin() + vchSig.size() - 1);
+ return CPubKey::CheckLowS(vchSigCopy);
}
bool static IsDefinedHashtypeSignature(const valtype &vchSig) {
diff --git a/src/test/test_bitcoin.h b/src/test/test_bitcoin.h
index 0bab4b6831..f657d72038 100644
--- a/src/test/test_bitcoin.h
+++ b/src/test/test_bitcoin.h
@@ -3,6 +3,7 @@
#include "chainparamsbase.h"
#include "key.h"
+#include "pubkey.h"
#include "txdb.h"
#include <boost/filesystem.hpp>
@@ -12,6 +13,8 @@
* This just configures logging and chain parameters.
*/
struct BasicTestingSetup {
+ ECCVerifyHandle globalVerifyHandle;
+
BasicTestingSetup(const std::string& chainName = CBaseChainParams::MAIN);
~BasicTestingSetup();
};