diff options
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/ecwrapper.cpp | 333 | ||||
-rw-r--r-- | src/ecwrapper.h | 46 | ||||
-rw-r--r-- | src/key.cpp | 333 |
4 files changed, 384 insertions, 330 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 155adfef7d..00da8b2372 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -82,6 +82,7 @@ BITCOIN_CORE_H = \ core_io.h \ crypter.h \ db.h \ + ecwrapper.h \ hash.h \ init.h \ key.h \ @@ -211,6 +212,7 @@ libbitcoin_common_a_SOURCES = \ core.cpp \ core_read.cpp \ core_write.cpp \ + ecwrapper.cpp \ hash.cpp \ key.cpp \ keystore.cpp \ diff --git a/src/ecwrapper.cpp b/src/ecwrapper.cpp new file mode 100644 index 0000000000..e5db670927 --- /dev/null +++ b/src/ecwrapper.cpp @@ -0,0 +1,333 @@ +// Copyright (c) 2009-2014 The Bitcoin developers +// Distributed under the MIT/X11 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 { + +// Generate a private key from just the secret parameter +int EC_KEY_regenerate_key(EC_KEY *eckey, BIGNUM *priv_key) +{ + int ok = 0; + BN_CTX *ctx = NULL; + EC_POINT *pub_key = NULL; + + if (!eckey) return 0; + + const EC_GROUP *group = EC_KEY_get0_group(eckey); + + if ((ctx = BN_CTX_new()) == NULL) + goto err; + + pub_key = EC_POINT_new(group); + + if (pub_key == NULL) + goto err; + + if (!EC_POINT_mul(group, pub_key, priv_key, NULL, NULL, ctx)) + goto err; + + EC_KEY_set_private_key(eckey,priv_key); + EC_KEY_set_public_key(eckey,pub_key); + + ok = 1; + +err: + + if (pub_key) + EC_POINT_free(pub_key); + if (ctx != NULL) + BN_CTX_free(ctx); + + return(ok); +} + +// 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::GetSecretBytes(unsigned char vch[32]) const { + const BIGNUM *bn = EC_KEY_get0_private_key(pkey); + assert(bn); + int nBytes = BN_num_bytes(bn); + int n=BN_bn2bin(bn,&vch[32 - nBytes]); + assert(n == nBytes); + memset(vch, 0, 32 - nBytes); +} + +void CECKey::SetSecretBytes(const unsigned char vch[32]) { + bool ret; + BIGNUM bn; + BN_init(&bn); + ret = BN_bin2bn(vch, 32, &bn) != NULL; + assert(ret); + ret = EC_KEY_regenerate_key(pkey, &bn) != 0; + assert(ret); + BN_clear_free(&bn); +} + +int CECKey::GetPrivKeySize(bool fCompressed) { + EC_KEY_set_conv_form(pkey, fCompressed ? POINT_CONVERSION_COMPRESSED : POINT_CONVERSION_UNCOMPRESSED); + return i2d_ECPrivateKey(pkey, NULL); +} +int CECKey::GetPrivKey(unsigned char* privkey, bool fCompressed) { + EC_KEY_set_conv_form(pkey, fCompressed ? POINT_CONVERSION_COMPRESSED : POINT_CONVERSION_UNCOMPRESSED); + return i2d_ECPrivateKey(pkey, &privkey); +} + +bool CECKey::SetPrivKey(const unsigned char* privkey, size_t size, bool fSkipCheck) { + if (d2i_ECPrivateKey(&pkey, &privkey, size)) { + if(fSkipCheck) + return true; + + // d2i_ECPrivateKey returns true if parsing succeeds. + // This doesn't necessarily mean the key is valid. + if (EC_KEY_check_key(pkey)) + return true; + } + return false; +} + +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::Sign(const uint256 &hash, std::vector<unsigned char>& vchSig, bool lowS) { + vchSig.clear(); + ECDSA_SIG *sig = ECDSA_do_sign((unsigned char*)&hash, sizeof(hash), pkey); + if (sig == NULL) + return false; + BN_CTX *ctx = BN_CTX_new(); + BN_CTX_start(ctx); + const EC_GROUP *group = EC_KEY_get0_group(pkey); + BIGNUM *order = BN_CTX_get(ctx); + BIGNUM *halforder = BN_CTX_get(ctx); + EC_GROUP_get_order(group, order, ctx); + BN_rshift1(halforder, order); + if (lowS && BN_cmp(sig->s, halforder) > 0) { + // enforce low S values, by negating the value (modulo the order) if above order/2. + BN_sub(sig->s, order, sig->s); + } + BN_CTX_end(ctx); + BN_CTX_free(ctx); + unsigned int nSize = ECDSA_size(pkey); + vchSig.resize(nSize); // Make sure it is big enough + unsigned char *pos = &vchSig[0]; + nSize = i2d_ECDSA_SIG(sig, &pos); + ECDSA_SIG_free(sig); + vchSig.resize(nSize); // Shrink to fit actual size + return true; +} + +bool CECKey::Verify(const uint256 &hash, const std::vector<unsigned char>& vchSig) { + // -1 = error, 0 = bad sig, 1 = good + if (ECDSA_verify(0, (unsigned char*)&hash, sizeof(hash), &vchSig[0], vchSig.size(), pkey) != 1) + return false; + return true; +} + +bool CECKey::SignCompact(const uint256 &hash, unsigned char *p64, int &rec) { + bool fOk = false; + ECDSA_SIG *sig = ECDSA_do_sign((unsigned char*)&hash, sizeof(hash), pkey); + if (sig==NULL) + return false; + memset(p64, 0, 64); + int nBitsR = BN_num_bits(sig->r); + int nBitsS = BN_num_bits(sig->s); + if (nBitsR <= 256 && nBitsS <= 256) { + std::vector<unsigned char> pubkey; + GetPubKey(pubkey, true); + for (int i=0; i<4; i++) { + CECKey keyRec; + if (ECDSA_SIG_recover_key_GFp(keyRec.pkey, sig, (unsigned char*)&hash, sizeof(hash), i, 1) == 1) { + std::vector<unsigned char> pubkeyRec; + keyRec.GetPubKey(pubkeyRec, true); + if (pubkeyRec == pubkey) { + rec = i; + fOk = true; + break; + } + } + } + assert(fOk); + BN_bn2bin(sig->r,&p64[32-(nBitsR+7)/8]); + BN_bn2bin(sig->s,&p64[64-(nBitsS+7)/8]); + } + ECDSA_SIG_free(sig); + return fOk; +} + +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::TweakSecret(unsigned char vchSecretOut[32], const unsigned char vchSecretIn[32], const unsigned char vchTweak[32]) +{ + bool ret = true; + BN_CTX *ctx = BN_CTX_new(); + BN_CTX_start(ctx); + BIGNUM *bnSecret = BN_CTX_get(ctx); + BIGNUM *bnTweak = BN_CTX_get(ctx); + BIGNUM *bnOrder = BN_CTX_get(ctx); + EC_GROUP *group = EC_GROUP_new_by_curve_name(NID_secp256k1); + 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 + BN_bin2bn(vchSecretIn, 32, bnSecret); + BN_add(bnSecret, bnSecret, bnTweak); + BN_nnmod(bnSecret, bnSecret, bnOrder, ctx); + if (BN_is_zero(bnSecret)) + ret = false; // ridiculously unlikely + int nBits = BN_num_bits(bnSecret); + memset(vchSecretOut, 0, 32); + BN_bn2bin(bnSecret, &vchSecretOut[32-(nBits+7)/8]); + EC_GROUP_free(group); + BN_CTX_end(ctx); + BN_CTX_free(ctx); + 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 new file mode 100644 index 0000000000..072da4a942 --- /dev/null +++ b/src/ecwrapper.h @@ -0,0 +1,46 @@ +// Copyright (c) 2009-2014 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_EC_WRAPPER_H +#define BITCOIN_EC_WRAPPER_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 GetSecretBytes(unsigned char vch[32]) const; + void SetSecretBytes(const unsigned char vch[32]); + int GetPrivKeySize(bool fCompressed); + int GetPrivKey(unsigned char* privkey, bool fCompressed); + bool SetPrivKey(const unsigned char* privkey, size_t size, bool fSkipCheck=false); + void GetPubKey(std::vector<unsigned char>& pubkey, bool fCompressed); + bool SetPubKey(const unsigned char* pubkey, size_t size); + bool Sign(const uint256 &hash, std::vector<unsigned char>& vchSig, bool lowS); + bool Verify(const uint256 &hash, const std::vector<unsigned char>& vchSig); + bool SignCompact(const uint256 &hash, unsigned char *p64, int &rec); + + // 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); + + static bool TweakSecret(unsigned char vchSecretOut[32], const unsigned char vchSecretIn[32], const unsigned char vchTweak[32]); + bool TweakPublic(const unsigned char vchTweak[32]); + static bool SanityCheck(); +}; + +#endif diff --git a/src/key.cpp b/src/key.cpp index d2001717f1..0f4bc6652c 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -10,12 +10,10 @@ #ifdef USE_SECP256K1 #include <secp256k1.h> #else -#include <openssl/bn.h> -#include <openssl/ecdsa.h> -#include <openssl/obj_mac.h> +#include "ecwrapper.h" #endif -// anonymous namespace with local implementation code (OpenSSL interaction) +// anonymous namespace namespace { #ifdef USE_SECP256K1 @@ -31,325 +29,6 @@ public: }; 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) -{ - int ok = 0; - BN_CTX *ctx = NULL; - EC_POINT *pub_key = NULL; - - if (!eckey) return 0; - - const EC_GROUP *group = EC_KEY_get0_group(eckey); - - if ((ctx = BN_CTX_new()) == NULL) - goto err; - - pub_key = EC_POINT_new(group); - - if (pub_key == NULL) - goto err; - - if (!EC_POINT_mul(group, pub_key, priv_key, NULL, NULL, ctx)) - goto err; - - EC_KEY_set_private_key(eckey,priv_key); - EC_KEY_set_public_key(eckey,pub_key); - - ok = 1; - -err: - - if (pub_key) - EC_POINT_free(pub_key); - if (ctx != NULL) - BN_CTX_free(ctx); - - return(ok); -} - -// 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; -} - -// RAII Wrapper around OpenSSL's EC_KEY -class CECKey { -private: - EC_KEY *pkey; - -public: - CECKey() { - pkey = EC_KEY_new_by_curve_name(NID_secp256k1); - assert(pkey != NULL); - } - - ~CECKey() { - EC_KEY_free(pkey); - } - - void GetSecretBytes(unsigned char vch[32]) const { - const BIGNUM *bn = EC_KEY_get0_private_key(pkey); - assert(bn); - int nBytes = BN_num_bytes(bn); - int n=BN_bn2bin(bn,&vch[32 - nBytes]); - assert(n == nBytes); - memset(vch, 0, 32 - nBytes); - } - - void SetSecretBytes(const unsigned char vch[32]) { - bool ret; - BIGNUM bn; - BN_init(&bn); - ret = BN_bin2bn(vch, 32, &bn) != NULL; - assert(ret); - ret = EC_KEY_regenerate_key(pkey, &bn) != 0; - assert(ret); - BN_clear_free(&bn); - } - - int GetPrivKeySize(bool fCompressed) { - EC_KEY_set_conv_form(pkey, fCompressed ? POINT_CONVERSION_COMPRESSED : POINT_CONVERSION_UNCOMPRESSED); - return i2d_ECPrivateKey(pkey, NULL); - } - int GetPrivKey(unsigned char* privkey, bool fCompressed) { - EC_KEY_set_conv_form(pkey, fCompressed ? POINT_CONVERSION_COMPRESSED : POINT_CONVERSION_UNCOMPRESSED); - return i2d_ECPrivateKey(pkey, &privkey); - } - - bool SetPrivKey(const unsigned char* privkey, size_t size, bool fSkipCheck=false) { - if (d2i_ECPrivateKey(&pkey, &privkey, size)) { - if(fSkipCheck) - return true; - - // d2i_ECPrivateKey returns true if parsing succeeds. - // This doesn't necessarily mean the key is valid. - if (EC_KEY_check_key(pkey)) - return true; - } - return false; - } - - void 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 SetPubKey(const unsigned char* pubkey, size_t size) { - return o2i_ECPublicKey(&pkey, &pubkey, size) != NULL; - } - - bool Sign(const uint256 &hash, std::vector<unsigned char>& vchSig, bool lowS) { - vchSig.clear(); - ECDSA_SIG *sig = ECDSA_do_sign((unsigned char*)&hash, sizeof(hash), pkey); - if (sig == NULL) - return false; - BN_CTX *ctx = BN_CTX_new(); - BN_CTX_start(ctx); - const EC_GROUP *group = EC_KEY_get0_group(pkey); - BIGNUM *order = BN_CTX_get(ctx); - BIGNUM *halforder = BN_CTX_get(ctx); - EC_GROUP_get_order(group, order, ctx); - BN_rshift1(halforder, order); - if (lowS && BN_cmp(sig->s, halforder) > 0) { - // enforce low S values, by negating the value (modulo the order) if above order/2. - BN_sub(sig->s, order, sig->s); - } - BN_CTX_end(ctx); - BN_CTX_free(ctx); - unsigned int nSize = ECDSA_size(pkey); - vchSig.resize(nSize); // Make sure it is big enough - unsigned char *pos = &vchSig[0]; - nSize = i2d_ECDSA_SIG(sig, &pos); - ECDSA_SIG_free(sig); - vchSig.resize(nSize); // Shrink to fit actual size - return true; - } - - bool Verify(const uint256 &hash, const std::vector<unsigned char>& vchSig) { - // -1 = error, 0 = bad sig, 1 = good - if (ECDSA_verify(0, (unsigned char*)&hash, sizeof(hash), &vchSig[0], vchSig.size(), pkey) != 1) - return false; - return true; - } - - bool SignCompact(const uint256 &hash, unsigned char *p64, int &rec) { - bool fOk = false; - ECDSA_SIG *sig = ECDSA_do_sign((unsigned char*)&hash, sizeof(hash), pkey); - if (sig==NULL) - return false; - memset(p64, 0, 64); - int nBitsR = BN_num_bits(sig->r); - int nBitsS = BN_num_bits(sig->s); - if (nBitsR <= 256 && nBitsS <= 256) { - std::vector<unsigned char> pubkey; - GetPubKey(pubkey, true); - for (int i=0; i<4; i++) { - CECKey keyRec; - if (ECDSA_SIG_recover_key_GFp(keyRec.pkey, sig, (unsigned char*)&hash, sizeof(hash), i, 1) == 1) { - std::vector<unsigned char> pubkeyRec; - keyRec.GetPubKey(pubkeyRec, true); - if (pubkeyRec == pubkey) { - rec = i; - fOk = true; - break; - } - } - } - assert(fOk); - BN_bn2bin(sig->r,&p64[32-(nBitsR+7)/8]); - BN_bn2bin(sig->s,&p64[64-(nBitsS+7)/8]); - } - ECDSA_SIG_free(sig); - return fOk; - } - - // 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) - { - 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; - } - - static bool TweakSecret(unsigned char vchSecretOut[32], const unsigned char vchSecretIn[32], const unsigned char vchTweak[32]) - { - bool ret = true; - BN_CTX *ctx = BN_CTX_new(); - BN_CTX_start(ctx); - BIGNUM *bnSecret = BN_CTX_get(ctx); - BIGNUM *bnTweak = BN_CTX_get(ctx); - BIGNUM *bnOrder = BN_CTX_get(ctx); - EC_GROUP *group = EC_GROUP_new_by_curve_name(NID_secp256k1); - 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 - BN_bin2bn(vchSecretIn, 32, bnSecret); - BN_add(bnSecret, bnSecret, bnTweak); - BN_nnmod(bnSecret, bnSecret, bnOrder, ctx); - if (BN_is_zero(bnSecret)) - ret = false; // ridiculously unlikely - int nBits = BN_num_bits(bnSecret); - memset(vchSecretOut, 0, 32); - BN_bn2bin(bnSecret, &vchSecretOut[32-(nBits+7)/8]); - EC_GROUP_free(group); - BN_CTX_end(ctx); - BN_CTX_free(ctx); - return ret; - } - - bool 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; - } -}; - #endif int CompareBigEndian(const unsigned char *c1, size_t c1len, const unsigned char *c2, size_t c2len) { @@ -746,12 +425,6 @@ 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; - EC_KEY_free(pkey); - - // TODO Is there more EC functionality that could be missing? - return true; + return CECKey::SanityCheck(); #endif } |