aboutsummaryrefslogtreecommitdiff
path: root/src/pubkey.cpp
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 /src/pubkey.cpp
parentb632145edeb376b4d1597f192ca00634f7d2866c (diff)
Switch to libsecp256k1-based validation for ECDSA
Diffstat (limited to 'src/pubkey.cpp')
-rw-r--r--src/pubkey.cpp260
1 files changed, 233 insertions, 27 deletions
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;
+ }
+}