From 6fd7ef2bbf1f941c8dee302ffdeb44e603148723 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Fri, 7 Feb 2014 02:19:48 +0100 Subject: Also switch the (unused) verification code to low-s instead of even-s. a81cd968 introduced a malleability breaker for signatures (using an even value for S). In e0e14e43 this was changed to the lower of two potential values, rather than the even one. Only the signing code was changed though, the (for now unused) verification code wasn't adapted. --- src/key.cpp | 72 +++++++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 51 insertions(+), 21 deletions(-) (limited to 'src/key.cpp') diff --git a/src/key.cpp b/src/key.cpp index b57b7c506..2199996cf 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -332,30 +332,60 @@ public: } }; +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[0] = {}; + + }; // end of anonymous namespace bool CKey::Check(const unsigned char *vch) { - // Do not convert to OpenSSL's data structures for range-checking keys, - // it's easy enough to do directly. - static const unsigned char vchMax[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 - }; - bool fIsZero = true; - for (int i=0; i<32 && fIsZero; i++) - if (vch[i] != 0) - fIsZero = false; - if (fIsZero) - return false; - for (int i=0; i<32; i++) { - if (vch[i] < vchMax[i]) - return true; - if (vch[i] > vchMax[i]) - return false; - } - return true; + return CompareBigEndian(vch, 32, vchZero, 0) > 0 && + CompareBigEndian(vch, 32, vchMaxModOrder, 32) <= 0; +} + +bool CKey::CheckSignatureElement(const unsigned char *vch, int len, bool half) { + return CompareBigEndian(vch, len, vchZero, 0) > 0 && + CompareBigEndian(vch, len, half ? vchMaxModHalfOrder : vchMaxModOrder, 32) <= 0; } void CKey::MakeNewKey(bool fCompressedIn) { -- cgit v1.2.3 From f40dbeedde3a6a73c13b0241258cf23248db1de1 Mon Sep 17 00:00:00 2001 From: Kamil Domanski Date: Tue, 13 May 2014 21:41:51 +0200 Subject: remove CPubKey::VerifyCompact( ) which is never used --- src/key.cpp | 15 --------------- 1 file changed, 15 deletions(-) (limited to 'src/key.cpp') diff --git a/src/key.cpp b/src/key.cpp index 2199996cf..aa24f0a62 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -485,21 +485,6 @@ bool CPubKey::RecoverCompact(const uint256 &hash, const std::vector& vchSig) const { - if (!IsValid()) - return false; - if (vchSig.size() != 65) - return false; - CECKey key; - if (!key.Recover(hash, &vchSig[1], (vchSig[0] - 27) & ~4)) - return false; - CPubKey pubkeyRec; - key.GetPubKey(pubkeyRec, IsCompressed()); - if (*this != pubkeyRec) - return false; - return true; -} - bool CPubKey::IsFullyValid() const { if (!IsValid()) return false; -- cgit v1.2.3 From 4a09e1df51267c6d2dec219c6f96a24b716cc251 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Mon, 2 Jun 2014 16:21:03 -0700 Subject: key.cpp: fail with a friendlier message on missing ssl EC support Previously if bitcoind is linked with an OpenSSL which is compiled without EC support, this is seen as an assertion failure "pKey != NULL" at key.cpp:134, which occurs after several seconds. It is an esoteric piece of knowledge to interpret this as "oops, I linked with the wrong OpenSSL", and because of the delay it may not even be noticed. The new output is : OpenSSL appears to lack support for elliptic curve cryptography. For more information, visit https://en.bitcoin.it/wiki/OpenSSL_and_EC_Libraries : Initialization sanity check failed. Bitcoin Core is shutting down. which occurs immediately after attempted startup. This also blocks in an InitSanityCheck() function which currently only checks for EC support but should eventually do more. See #4081. --- src/key.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'src/key.cpp') diff --git a/src/key.cpp b/src/key.cpp index aa24f0a62..4747beffb 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -631,3 +631,15 @@ bool CExtPubKey::Derive(CExtPubKey &out, unsigned int nChild) const { out.nChild = nChild; return pubkey.Derive(out.pubkey, out.vchChainCode, nChild, vchChainCode); } + +bool ECC_InitSanityCheck() { + 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; +} + + -- cgit v1.2.3 From 977cdadea8a77eed04f1f0fd341ba9dedc3fa783 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Sun, 20 Apr 2014 17:36:25 +0200 Subject: Add a built-in SHA256/SHA512 implementation. This also moves the HMAC-SHA512 implementation to sha2.cpp. --- src/key.cpp | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) (limited to 'src/key.cpp') diff --git a/src/key.cpp b/src/key.cpp index 4747beffb..068594892 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -4,6 +4,8 @@ #include "key.h" +#include "sha2.h" + #include #include #include @@ -510,12 +512,10 @@ void static BIP32Hash(const unsigned char chainCode[32], unsigned int nChild, un num[1] = (nChild >> 16) & 0xFF; num[2] = (nChild >> 8) & 0xFF; num[3] = (nChild >> 0) & 0xFF; - HMAC_SHA512_CTX ctx; - HMAC_SHA512_Init(&ctx, chainCode, 32); - HMAC_SHA512_Update(&ctx, &header, 1); - HMAC_SHA512_Update(&ctx, data, 32); - HMAC_SHA512_Update(&ctx, num, 4); - HMAC_SHA512_Final(output, &ctx); + CHMAC_SHA512(chainCode, 32).Write(&header, 1) + .Write(data, 32) + .Write(num, 4) + .Finalize(output); } bool CKey::Derive(CKey& keyChild, unsigned char ccChild[32], unsigned int nChild, const unsigned char cc[32]) const { @@ -562,13 +562,10 @@ bool CExtKey::Derive(CExtKey &out, unsigned int nChild) const { } void CExtKey::SetMaster(const unsigned char *seed, unsigned int nSeedLen) { - static const char hashkey[] = {'B','i','t','c','o','i','n',' ','s','e','e','d'}; - HMAC_SHA512_CTX ctx; - HMAC_SHA512_Init(&ctx, hashkey, sizeof(hashkey)); - HMAC_SHA512_Update(&ctx, seed, nSeedLen); + static const unsigned char hashkey[] = {'B','i','t','c','o','i','n',' ','s','e','e','d'}; unsigned char out[64]; LockObject(out); - HMAC_SHA512_Final(out, &ctx); + CHMAC_SHA512(hashkey, sizeof(hashkey)).Write(seed, nSeedLen).Finalize(out); key.Set(&out[0], &out[32], true); memcpy(vchChainCode, &out[32], 32); UnlockObject(out); -- cgit v1.2.3 From 13b5dfef64bbb77d583b2acc59e2b33f89645308 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Mon, 28 Apr 2014 12:20:15 +0200 Subject: Move crypto implementations to src/crypto/ --- src/key.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/key.cpp') diff --git a/src/key.cpp b/src/key.cpp index 068594892..96b1ac439 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -4,7 +4,7 @@ #include "key.h" -#include "sha2.h" +#include "crypto/sha2.h" #include #include -- cgit v1.2.3 From e10dcf27b4fb324a85b5650c7acd2ea5a52e822f Mon Sep 17 00:00:00 2001 From: Philip Kaufmann Date: Tue, 24 Jun 2014 14:17:43 +0200 Subject: ensure clean and consistent "namespace" usage - remove some missplaced ; - ensure end of a namespace is clearly visible - use same formatting when using namespace --- src/key.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/key.cpp') diff --git a/src/key.cpp b/src/key.cpp index 96b1ac439..784085da3 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -377,8 +377,7 @@ const unsigned char vchMaxModHalfOrder[32] = { const unsigned char vchZero[0] = {}; - -}; // end of anonymous namespace +} // anon namespace bool CKey::Check(const unsigned char *vch) { return CompareBigEndian(vch, 32, vchZero, 0) > 0 && -- cgit v1.2.3 From fda3fed18aedc4bfc8ccffe89d8d2cabb12677ab Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Fri, 6 Jun 2014 01:26:27 +0200 Subject: libsecp256k1 integration --- src/key.cpp | 130 +++++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 119 insertions(+), 11 deletions(-) (limited to 'src/key.cpp') diff --git a/src/key.cpp b/src/key.cpp index 784085da3..3c4fa77e7 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -5,15 +5,34 @@ #include "key.h" #include "crypto/sha2.h" +#include +#ifdef USE_SECP256K1 +#include +#else #include #include #include -#include +#endif // anonymous namespace with local implementation code (OpenSSL interaction) namespace { +#ifdef USE_SECP256K1 +#include +class CSecp256k1Init { +public: + CSecp256k1Init() { + secp256k1_start(); + } + ~CSecp256k1Init() { + secp256k1_stop(); + } +}; +static CSecp256k1Init instance_of_csecp256k1; + +#else + // Generate a private key from just the secret parameter int EC_KEY_regenerate_key(EC_KEY *eckey, BIGNUM *priv_key) { @@ -334,6 +353,8 @@ public: } }; +#endif + int CompareBigEndian(const unsigned char *c1, size_t c1len, const unsigned char *c2, size_t c2len) { while (c1len > c2len) { if (*c1) @@ -398,10 +419,15 @@ void CKey::MakeNewKey(bool fCompressedIn) { } bool CKey::SetPrivKey(const CPrivKey &privkey, bool fCompressedIn) { +#ifdef USE_SECP256K1 + if (!secp256k1_ecdsa_privkey_import((unsigned char*)begin(), &privkey[0], privkey.size())) + return false; +#else CECKey key; if (!key.SetPrivKey(privkey)) return false; key.GetSecretBytes(vch); +#endif fCompressed = fCompressedIn; fValid = true; return true; @@ -409,99 +435,167 @@ bool CKey::SetPrivKey(const CPrivKey &privkey, bool fCompressedIn) { CPrivKey CKey::GetPrivKey() const { assert(fValid); + CPrivKey privkey; +#ifdef USE_SECP256K1 + privkey.resize(279); + int privkeylen = 279; + int ret = secp256k1_ecdsa_privkey_export(begin(), (unsigned char*)&privkey[0], &privkeylen, fCompressed); + assert(ret); + privkey.resize(privkeylen); +#else CECKey key; key.SetSecretBytes(vch); - CPrivKey privkey; key.GetPrivKey(privkey, fCompressed); +#endif return privkey; } CPubKey CKey::GetPubKey() const { assert(fValid); + CPubKey pubkey; +#ifdef USE_SECP256K1 + int clen = 65; + int ret = secp256k1_ecdsa_pubkey_create((unsigned char*)pubkey.begin(), &clen, begin(), fCompressed); + assert(ret); + assert(pubkey.IsValid()); + assert((int)pubkey.size() == clen); +#else CECKey key; key.SetSecretBytes(vch); - CPubKey pubkey; key.GetPubKey(pubkey, fCompressed); +#endif return pubkey; } bool CKey::Sign(const uint256 &hash, std::vector& vchSig) const { if (!fValid) return false; +#ifdef USE_SECP256K1 + vchSig.resize(72); + int nSigLen = 72; + CKey nonce; + do { + nonce.MakeNewKey(true); + if (secp256k1_ecdsa_sign((const unsigned char*)&hash, 32, (unsigned char*)&vchSig[0], &nSigLen, begin(), nonce.begin())) + break; + } while(true); + vchSig.resize(nSigLen); + return true; +#else CECKey key; key.SetSecretBytes(vch); return key.Sign(hash, vchSig); +#endif } bool CKey::SignCompact(const uint256 &hash, std::vector& vchSig) const { if (!fValid) return false; - CECKey key; - key.SetSecretBytes(vch); vchSig.resize(65); int rec = -1; +#ifdef USE_SECP256K1 + CKey nonce; + do { + nonce.MakeNewKey(true); + if (secp256k1_ecdsa_sign_compact((const unsigned char*)&hash, 32, &vchSig[1], begin(), nonce.begin(), &rec)) + break; + } while(true); +#else + CECKey key; + key.SetSecretBytes(vch); if (!key.SignCompact(hash, &vchSig[1], rec)) return false; +#endif assert(rec != -1); vchSig[0] = 27 + rec + (fCompressed ? 4 : 0); return true; } bool CKey::Load(CPrivKey &privkey, CPubKey &vchPubKey, bool fSkipCheck=false) { +#ifdef USE_SECP256K1 + if (!secp256k1_ecdsa_privkey_import((unsigned char*)begin(), &privkey[0], privkey.size())) + return false; +#else CECKey key; if (!key.SetPrivKey(privkey, fSkipCheck)) return false; - key.GetSecretBytes(vch); +#endif fCompressed = vchPubKey.IsCompressed(); fValid = true; - + if (fSkipCheck) return true; - + if (GetPubKey() != vchPubKey) return false; - + return true; } 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) + return false; +#else CECKey key; if (!key.SetPubKey(*this)) return false; if (!key.Verify(hash, vchSig)) return false; +#endif return true; } bool CPubKey::RecoverCompact(const uint256 &hash, const std::vector& vchSig) { if (vchSig.size() != 65) return false; + int recid = (vchSig[0] - 27) & 3; + bool fComp = (vchSig[0] - 27) & 4; +#ifdef USE_SECP256K1 + int pubkeylen = 65; + if (!secp256k1_ecdsa_recover_compact((const unsigned char*)&hash, 32, &vchSig[1], (unsigned char*)begin(), &pubkeylen, fComp, recid)) + return false; + assert((int)size() == pubkeylen); +#else CECKey key; - if (!key.Recover(hash, &vchSig[1], (vchSig[0] - 27) & ~4)) + if (!key.Recover(hash, &vchSig[1], recid)) return false; - key.GetPubKey(*this, (vchSig[0] - 27) & 4); + key.GetPubKey(*this, fComp); +#endif return true; } bool CPubKey::IsFullyValid() const { if (!IsValid()) return false; +#ifdef USE_SECP256K1 + if (!secp256k1_ecdsa_pubkey_verify(begin(), size())) + return false; +#else CECKey key; if (!key.SetPubKey(*this)) 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); + assert(ret); + assert(clen == (int)size()); +#else CECKey key; if (!key.SetPubKey(*this)) return false; key.GetPubKey(*this, false); +#endif return true; } @@ -531,7 +625,12 @@ bool CKey::Derive(CKey& keyChild, unsigned char ccChild[32], unsigned int nChild BIP32Hash(cc, nChild, 0, begin(), out); } memcpy(ccChild, out+32, 32); +#ifdef USE_SECP256K1 + memcpy((unsigned char*)keyChild.begin(), begin(), 32); + bool ret = secp256k1_ecdsa_privkey_tweak_add((unsigned char*)keyChild.begin(), out); +#else bool ret = CECKey::TweakSecret((unsigned char*)keyChild.begin(), begin(), out); +#endif UnlockObject(out); keyChild.fCompressed = true; keyChild.fValid = ret; @@ -545,10 +644,15 @@ 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(*this); ret &= key.TweakPublic(out); key.GetPubKey(pubkeyChild, true); +#endif return ret; } @@ -629,6 +733,9 @@ bool CExtPubKey::Derive(CExtPubKey &out, unsigned int nChild) const { } bool ECC_InitSanityCheck() { +#ifdef USE_SECP256K1 + return true; +#else EC_KEY *pkey = EC_KEY_new_by_curve_name(NID_secp256k1); if(pkey == NULL) return false; @@ -636,6 +743,7 @@ bool ECC_InitSanityCheck() { // TODO Is there more EC functionality that could be missing? return true; +#endif } -- cgit v1.2.3