Browse Source

Switch to libsecp256k1-based validation for ECDSA

0.13
Pieter Wuille 9 years ago
parent
commit
6e18268616
  1. 15
      doc/release-notes.md
  2. 10
      src/Makefile.am
  3. 10
      src/bitcoin-tx.cpp
  4. 68
      src/eccryptoverify.cpp
  5. 21
      src/eccryptoverify.h
  6. 218
      src/ecwrapper.cpp
  7. 40
      src/ecwrapper.h
  8. 6
      src/init.cpp
  9. 260
      src/pubkey.cpp
  10. 16
      src/pubkey.h
  11. 9
      src/script/bitcoinconsensus.cpp
  12. 13
      src/script/interpreter.cpp
  13. 3
      src/test/test_bitcoin.h

15
doc/release-notes.md

@ -190,6 +190,21 @@ calculating the target.
A more detailed documentation about keeping traffic low can be found in A more detailed documentation about keeping traffic low can be found in
[/doc/reducetraffic.md](/doc/reducetraffic.md). [/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 0.12.0 Change log
================= =================

10
src/Makefile.am

@ -104,8 +104,6 @@ BITCOIN_CORE_H = \
consensus/validation.h \ consensus/validation.h \
core_io.h \ core_io.h \
core_memusage.h \ core_memusage.h \
eccryptoverify.h \
ecwrapper.h \
hash.h \ hash.h \
httprpc.h \ httprpc.h \
httpserver.h \ httpserver.h \
@ -272,8 +270,6 @@ libbitcoin_common_a_SOURCES = \
compressor.cpp \ compressor.cpp \
core_read.cpp \ core_read.cpp \
core_write.cpp \ core_write.cpp \
eccryptoverify.cpp \
ecwrapper.cpp \
hash.cpp \ hash.cpp \
key.cpp \ key.cpp \
keystore.cpp \ keystore.cpp \
@ -404,8 +400,6 @@ libbitcoinconsensus_la_SOURCES = \
crypto/sha1.cpp \ crypto/sha1.cpp \
crypto/sha256.cpp \ crypto/sha256.cpp \
crypto/sha512.cpp \ crypto/sha512.cpp \
eccryptoverify.cpp \
ecwrapper.cpp \
hash.cpp \ hash.cpp \
primitives/transaction.cpp \ primitives/transaction.cpp \
pubkey.cpp \ pubkey.cpp \
@ -420,8 +414,8 @@ if GLIBC_BACK_COMPAT
endif endif
libbitcoinconsensus_la_LDFLAGS = $(AM_LDFLAGS) -no-undefined $(RELDFLAGS) libbitcoinconsensus_la_LDFLAGS = $(AM_LDFLAGS) -no-undefined $(RELDFLAGS)
libbitcoinconsensus_la_LIBADD = $(CRYPTO_LIBS) libbitcoinconsensus_la_LIBADD = $(LIBSECP256K1)
libbitcoinconsensus_la_CPPFLAGS = $(AM_CPPFLAGS) $(CRYPTO_CFLAGS) -I$(builddir)/obj -DBUILD_BITCOIN_INTERNAL libbitcoinconsensus_la_CPPFLAGS = $(AM_CPPFLAGS) -I$(builddir)/obj -I$(srcdir)/secp256k1/include -DBUILD_BITCOIN_INTERNAL
libbitcoinconsensus_la_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) libbitcoinconsensus_la_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
endif endif

10
src/bitcoin-tx.cpp

@ -477,9 +477,15 @@ static void MutateTxSign(CMutableTransaction& tx, const string& flagStr)
class Secp256k1Init class Secp256k1Init
{ {
ECCVerifyHandle globalVerifyHandle;
public: public:
Secp256k1Init() { ECC_Start(); } Secp256k1Init() {
~Secp256k1Init() { ECC_Stop(); } ECC_Start();
}
~Secp256k1Init() {
ECC_Stop();
}
}; };
static void MutateTx(CMutableTransaction& tx, const string& command, static void MutateTx(CMutableTransaction& tx, const string& command,

68
src/eccryptoverify.cpp

@ -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

21
src/eccryptoverify.h

@ -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

218
src/ecwrapper.cpp

@ -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;
}

40
src/ecwrapper.h

@ -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

6
src/init.cpp

@ -154,6 +154,7 @@ public:
static CCoinsViewDB *pcoinsdbview = NULL; static CCoinsViewDB *pcoinsdbview = NULL;
static CCoinsViewErrorCatcher *pcoinscatcher = NULL; static CCoinsViewErrorCatcher *pcoinscatcher = NULL;
static boost::scoped_ptr<ECCVerifyHandle> globalVerifyHandle;
void Interrupt(boost::thread_group& threadGroup) void Interrupt(boost::thread_group& threadGroup)
{ {
@ -243,6 +244,7 @@ void Shutdown()
delete pwalletMain; delete pwalletMain;
pwalletMain = NULL; pwalletMain = NULL;
#endif #endif
globalVerifyHandle.reset();
ECC_Stop(); ECC_Stop();
LogPrintf("%s: done\n", __func__); LogPrintf("%s: done\n", __func__);
} }
@ -649,8 +651,7 @@ void ThreadImport(std::vector<boost::filesystem::path> vImportFiles)
bool InitSanityCheck(void) bool InitSanityCheck(void)
{ {
if(!ECC_InitSanityCheck()) { if(!ECC_InitSanityCheck()) {
InitError("OpenSSL appears to lack support for elliptic curve cryptography. For more " InitError("Elliptic curve cryptography sanity check failure. Aborting.");
"information, visit https://en.bitcoin.it/wiki/OpenSSL_and_EC_Libraries");
return false; return false;
} }
if (!glibc_sanity_test() || !glibcxx_sanity_test()) if (!glibc_sanity_test() || !glibcxx_sanity_test())
@ -991,6 +992,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
// Initialize elliptic curve code // Initialize elliptic curve code
ECC_Start(); ECC_Start();
globalVerifyHandle.reset(new ECCVerifyHandle());
// Sanity check // Sanity check
if (!InitSanityCheck()) if (!InitSanityCheck())

260
src/pubkey.cpp

@ -4,19 +4,184 @@
#include "pubkey.h" #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 { bool CPubKey::Verify(const uint256 &hash, const std::vector<unsigned char>& vchSig) const {
if (!IsValid()) if (!IsValid())
return false; return false;
CECKey key; secp256k1_pubkey pubkey;
if (!key.SetPubKey(begin(), size())) secp256k1_ecdsa_signature sig;
if (!secp256k1_ec_pubkey_parse(secp256k1_context_verify, &pubkey, &(*this)[0], size())) {
return false; return false;
if (!key.Verify(hash, vchSig)) }
if (vchSig.size() == 0) {
return false; 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) { 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; return false;
int recid = (vchSig[0] - 27) & 3; int recid = (vchSig[0] - 27) & 3;
bool fComp = ((vchSig[0] - 27) & 4) != 0; bool fComp = ((vchSig[0] - 27) & 4) != 0;
CECKey key; secp256k1_pubkey pubkey;
if (!key.Recover(hash, &vchSig[1], recid)) secp256k1_ecdsa_recoverable_signature sig;
if (!secp256k1_ecdsa_recoverable_signature_parse_compact(secp256k1_context_verify, &sig, &vchSig[1], recid)) {
return false;
}
if (!secp256k1_ecdsa_recover(secp256k1_context_verify, &pubkey, &sig, hash.begin())) {
return false; return false;
std::vector<unsigned char> pubkey; }
key.GetPubKey(pubkey, fComp); unsigned char pub[65];
Set(pubkey.begin(), pubkey.end()); 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; return true;
} }
bool CPubKey::IsFullyValid() const { bool CPubKey::IsFullyValid() const {
if (!IsValid()) if (!IsValid())
return false; return false;
CECKey key; secp256k1_pubkey pubkey;
if (!key.SetPubKey(begin(), size())) return secp256k1_ec_pubkey_parse(secp256k1_context_verify, &pubkey, &(*this)[0], size());
return false;
return true;
} }
bool CPubKey::Decompress() { bool CPubKey::Decompress() {
if (!IsValid()) if (!IsValid())
return false; return false;
CECKey key; secp256k1_pubkey pubkey;
if (!key.SetPubKey(begin(), size())) if (!secp256k1_ec_pubkey_parse(secp256k1_context_verify, &pubkey, &(*this)[0], size())) {
return false; return false;
std::vector<unsigned char> pubkey; }
key.GetPubKey(pubkey, false); unsigned char pub[65];
Set(pubkey.begin(), pubkey.end()); size_t publen = 65;
secp256k1_ec_pubkey_serialize(secp256k1_context_verify, pub, &publen, &pubkey, SECP256K1_EC_UNCOMPRESSED);
Set(pub, pub + publen);
return true; return true;
} }
@ -61,13 +232,18 @@ bool CPubKey::Derive(CPubKey& pubkeyChild, ChainCode &ccChild, unsigned int nChi
unsigned char out[64]; unsigned char out[64];
BIP32Hash(cc, nChild, *begin(), begin()+1, out); BIP32Hash(cc, nChild, *begin(), begin()+1, out);
memcpy(ccChild.begin(), out+32, 32); memcpy(ccChild.begin(), out+32, 32);
CECKey key; secp256k1_pubkey pubkey;
bool ret = key.SetPubKey(begin(), size()); if (!secp256k1_ec_pubkey_parse(secp256k1_context_verify, &pubkey, &(*this)[0], size())) {
ret &= key.TweakPublic(out); return false;
std::vector<unsigned char> pubkey; }
key.GetPubKey(pubkey, true); if (!secp256k1_ec_pubkey_tweak_add(secp256k1_context_verify, &pubkey, out)) {
pubkeyChild.Set(pubkey.begin(), pubkey.end()); return false;
return ret; }
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 { void CExtPubKey::Encode(unsigned char code[74]) const {
@ -95,3 +271,33 @@ bool CExtPubKey::Derive(CExtPubKey &out, unsigned int nChild) const {
out.nChild = nChild; out.nChild = nChild;
return pubkey.Derive(out.pubkey, out.chaincode, nChild, chaincode); 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;
}
}

16
src/pubkey.h

@ -177,6 +177,11 @@ public:
*/ */
bool Verify(const uint256& hash, const std::vector<unsigned char>& vchSig) const; 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. //! Recover a public key from a compact signature.
bool RecoverCompact(const uint256& hash, const std::vector<unsigned char>& vchSig); 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; 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 #endif // BITCOIN_PUBKEY_H

9
src/script/bitcoinconsensus.cpp

@ -6,6 +6,7 @@
#include "bitcoinconsensus.h" #include "bitcoinconsensus.h"
#include "primitives/transaction.h" #include "primitives/transaction.h"
#include "pubkey.h"
#include "script/interpreter.h" #include "script/interpreter.h"
#include "version.h" #include "version.h"
@ -60,7 +61,13 @@ inline int set_error(bitcoinconsensus_error* ret, bitcoinconsensus_error serror)
return 0; return 0;
} }
} // anon namespace struct ECCryptoClosure
{
ECCVerifyHandle handle;
};
ECCryptoClosure instance_of_eccryptoclosure;
}
int bitcoinconsensus_verify_script(const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen, int bitcoinconsensus_verify_script(const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen,
const unsigned char *txTo , unsigned int txToLen, const unsigned char *txTo , unsigned int txToLen,

13
src/script/interpreter.cpp

@ -9,7 +9,6 @@
#include "crypto/ripemd160.h" #include "crypto/ripemd160.h"
#include "crypto/sha1.h" #include "crypto/sha1.h"
#include "crypto/sha256.h" #include "crypto/sha256.h"
#include "eccryptoverify.h"
#include "pubkey.h" #include "pubkey.h"
#include "script/script.h" #include "script/script.h"
#include "uint256.h" #include "uint256.h"
@ -165,16 +164,8 @@ bool static IsLowDERSignature(const valtype &vchSig, ScriptError* serror) {
if (!IsValidSignatureEncoding(vchSig)) { if (!IsValidSignatureEncoding(vchSig)) {
return set_error(serror, SCRIPT_ERR_SIG_DER); return set_error(serror, SCRIPT_ERR_SIG_DER);
} }
unsigned int nLenR = vchSig[3]; std::vector<unsigned char> vchSigCopy(vchSig.begin(), vchSig.begin() + vchSig.size() - 1);
unsigned int nLenS = vchSig[5+nLenR]; return CPubKey::CheckLowS(vchSigCopy);
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;
} }
bool static IsDefinedHashtypeSignature(const valtype &vchSig) { bool static IsDefinedHashtypeSignature(const valtype &vchSig) {

3
src/test/test_bitcoin.h

@ -3,6 +3,7 @@
#include "chainparamsbase.h" #include "chainparamsbase.h"
#include "key.h" #include "key.h"
#include "pubkey.h"
#include "txdb.h" #include "txdb.h"
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
@ -12,6 +13,8 @@
* This just configures logging and chain parameters. * This just configures logging and chain parameters.
*/ */
struct BasicTestingSetup { struct BasicTestingSetup {
ECCVerifyHandle globalVerifyHandle;
BasicTestingSetup(const std::string& chainName = CBaseChainParams::MAIN); BasicTestingSetup(const std::string& chainName = CBaseChainParams::MAIN);
~BasicTestingSetup(); ~BasicTestingSetup();
}; };

Loading…
Cancel
Save