diff --git a/README-ecc.md b/README-ecc.md index 8c1046a..3a557f7 100644 --- a/README-ecc.md +++ b/README-ecc.md @@ -7,14 +7,15 @@ wrapper classes handling elliptic curve cryptography. Two implementations of this interface exist. One is based on OpenSSL, the other is based on libsecp256k1 (see https://github.com/bitcoin/secp256k1 ). The implementation to be used is selected at compile time using the -cmake variable "ECC_IMPL". It can take two values, openssl or secp256k1 . -The default is "openssl". The alternative can be configured when invoking +cmake variable "ECC_IMPL". It can take one of three values, openssl or +secp256k1 or mixed . +The default is "openssl". The alternatives can be configured when invoking cmake, for example cmake -D ECC_IMPL=secp256k1 . -If secp256k1 is chosen, the secp256k1 library and its include file must -already be installed in the appropriate library / include directories on +If secp256k1 or mixed is chosen, the secp256k1 library and its include file +must already be installed in the appropriate library / include directories on your system. @@ -36,20 +37,7 @@ the current ECC backend are written to it. If the file does exist, intermediate results from the current ECC backend are compared with the file contents. -For a full round of interoperability testing, you need to do this: - -1. Build ecc_test with openssl backend. -2. Run "ecc_test test ecc.interop.openssl". -3. Run "ecc_test test ecc.interop.openssl" again, testing openssl against - itself. -4. Build ecc_test with secp256k1 backend. -5. Run "ecc_test test ecc.interop.secp256k1". -6. Run "ecc_test test ecc.interop.secp256k1" again, testing secp256k1 against - itself. -7. Run "ecc_test test ecc.interop.openssl", testing secp256k1 against openssl. -8. Build ecc_test with openssl backend. -9. Run "ecc_test test ecc.interop.secp256k1", testing openssl against secp256k1. - -None of the test runs should produce any output. The above steps are scripted -in tests//ecc-interop.sh . +For a full round of interoperability testing, you can use the script +tests/ecc-interop.sh . +None of the test runs should produce any output. diff --git a/src/crypto/_elliptic_mixed_openssl.cpp b/src/crypto/_elliptic_mixed_openssl.cpp new file mode 100644 index 0000000..d8602ab --- /dev/null +++ b/src/crypto/_elliptic_mixed_openssl.cpp @@ -0,0 +1,237 @@ +namespace detail +{ + void private_key_impl::free_key() + { + if( _key != nullptr ) + { + EC_KEY_free(_key); + _key = nullptr; + } + } + + EC_KEY* private_key_impl::dup_key( const EC_KEY* cpy ) + { + return EC_KEY_dup( cpy ); + } + + void private_key_impl::copy_key( EC_KEY* to, const EC_KEY* from ) + { + EC_KEY_copy( to, from ); + } +} + +static void * ecies_key_derivation(const void *input, size_t ilen, void *output, size_t *olen) +{ + if (*olen < SHA512_DIGEST_LENGTH) { + return NULL; + } + *olen = SHA512_DIGEST_LENGTH; + return (void*)SHA512((const unsigned char*)input, ilen, (unsigned char*)output); +} + +// 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 +static int ECDSA_SIG_recover_key_GFp(EC_KEY *eckey, ECDSA_SIG *ecsig, const unsigned char *msg, int msglen, int recid, int check) +{ + if (!eckey) FC_THROW_EXCEPTION( exception, "null key" ); + + 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; +} + +int static inline EC_KEY_regenerate_key(EC_KEY *eckey, const 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); +} + +private_key private_key::regenerate( const fc::sha256& secret ) +{ + private_key self; + self.my->_key = EC_KEY_new_by_curve_name( NID_secp256k1 ); + if( !self.my->_key ) FC_THROW_EXCEPTION( exception, "Unable to generate EC key" ); + + ssl_bignum bn; + BN_bin2bn( (const unsigned char*)&secret, 32, bn ); + + if( !EC_KEY_regenerate_key(self.my->_key,bn) ) + { + FC_THROW_EXCEPTION( exception, "unable to regenerate key" ); + } + return self; +} + +fc::sha256 private_key::get_secret()const +{ + return get_secret( my->_key ); +} + +private_key::private_key( EC_KEY* k ) +{ + my->_key = k; +} + +compact_signature private_key::sign_compact( const fc::sha256& digest )const +{ + try { + FC_ASSERT( my->_key != nullptr ); + auto my_pub_key = get_public_key().serialize(); // just for good measure + //ECDSA_SIG *sig = ECDSA_do_sign((unsigned char*)&digest, sizeof(digest), my->_key); + public_key_data key_data; + while( true ) + { + ecdsa_sig sig = ECDSA_do_sign((unsigned char*)&digest, sizeof(digest), my->_key); + + if (sig==nullptr) + FC_THROW_EXCEPTION( exception, "Unable to sign" ); + + compact_signature csig; + // memset( csig.data, 0, sizeof(csig) ); + + int nBitsR = BN_num_bits(sig->r); + int nBitsS = BN_num_bits(sig->s); + if (nBitsR <= 256 && nBitsS <= 256) + { + int nRecId = -1; + EC_KEY* key = EC_KEY_new_by_curve_name( NID_secp256k1 ); + FC_ASSERT( key ); + EC_KEY_set_conv_form( key, POINT_CONVERSION_COMPRESSED ); + for (int i=0; i<4; i++) + { + if (ECDSA_SIG_recover_key_GFp(key, sig, (unsigned char*)&digest, sizeof(digest), i, 1) == 1) + { + unsigned char* buffer = (unsigned char*) key_data.begin(); + i2o_ECPublicKey( key, &buffer ); // FIXME: questionable memory handling + if ( key_data == my_pub_key ) + { + nRecId = i; + break; + } + } + } + EC_KEY_free( key ); + + if (nRecId == -1) + { + FC_THROW_EXCEPTION( exception, "unable to construct recoverable key"); + } + unsigned char* result = nullptr; + auto bytes = i2d_ECDSA_SIG( sig, &result ); + auto lenR = result[3]; + auto lenS = result[5+lenR]; + //idump( (result[0])(result[1])(result[2])(result[3])(result[3+lenR])(result[4+lenR])(bytes)(lenR)(lenS) ); + if( lenR != 32 ) { free(result); continue; } + if( lenS != 32 ) { free(result); continue; } + //idump( (33-(nBitsR+7)/8) ); + //idump( (65-(nBitsS+7)/8) ); + //idump( (sizeof(csig) ) ); + memcpy( &csig.data[1], &result[4], lenR ); + memcpy( &csig.data[33], &result[6+lenR], lenS ); + //idump( (csig.data[33]) ); + //idump( (csig.data[1]) ); + free(result); + //idump( (nRecId) ); + csig.data[0] = nRecId+27+4;//(fCompressedPubKey ? 4 : 0); + /* + idump( (csig) ); + auto rlen = BN_bn2bin(sig->r,&csig.data[33-(nBitsR+7)/8]); + auto slen = BN_bn2bin(sig->s,&csig.data[65-(nBitsS+7)/8]); + idump( (rlen)(slen) ); + */ + } + return csig; + } // while true + } FC_RETHROW_EXCEPTIONS( warn, "sign ${digest}", ("digest", digest)("private_key",*this) ); +} diff --git a/src/crypto/_elliptic_mixed_secp256k1.cpp b/src/crypto/_elliptic_mixed_secp256k1.cpp new file mode 100644 index 0000000..b0e2792 --- /dev/null +++ b/src/crypto/_elliptic_mixed_secp256k1.cpp @@ -0,0 +1,101 @@ +namespace detail +{ + static void init_lib() { + static int init_s = 0; + static int init_o = init_openssl(); + if (!init_s) { + secp256k1_start(SECP256K1_START_VERIFY | SECP256K1_START_SIGN); + init_s = 1; + } + } + + void public_key_impl::free_key() + { + if( _key != nullptr ) + { + delete _key; + _key = nullptr; + } + } + + public_key_data* public_key_impl::dup_key( const public_key_data* cpy ) + { + return new public_key_data( *cpy ); + } + + void public_key_impl::copy_key( public_key_data* to, const public_key_data* from ) + { + *to = *from; + } +} + +public_key public_key::add( const fc::sha256& digest )const +{ + FC_ASSERT( my->_key != nullptr ); + public_key_data new_key; + memcpy( new_key.begin(), my->_key->begin(), new_key.size() ); + FC_ASSERT( secp256k1_ec_pubkey_tweak_add( (unsigned char*) new_key.begin(), new_key.size(), (unsigned char*) digest.data() ) ); + return public_key( new_key ); +} + +std::string public_key::to_base58() const +{ + FC_ASSERT( my->_key != nullptr ); + return to_base58( *my->_key ); +} + +public_key_data public_key::serialize()const +{ + FC_ASSERT( my->_key != nullptr ); + return *my->_key; +} + +public_key_point_data public_key::serialize_ecc_point()const +{ + FC_ASSERT( my->_key != nullptr ); + public_key_point_data dat; + unsigned int pk_len = my->_key->size(); + memcpy( dat.begin(), my->_key->begin(), pk_len ); + FC_ASSERT( secp256k1_ec_pubkey_decompress( (unsigned char *) dat.begin(), (int*) &pk_len ) ); + FC_ASSERT( pk_len == dat.size() ); + return dat; +} + +public_key::public_key( const public_key_point_data& dat ) +{ + const char* front = &dat.data[0]; + if( *front == 0 ){} + else + { + EC_KEY *key = EC_KEY_new_by_curve_name( NID_secp256k1 ); + key = o2i_ECPublicKey( &key, (const unsigned char**)&front, sizeof(dat) ); + FC_ASSERT( key ); + EC_KEY_set_conv_form( key, POINT_CONVERSION_COMPRESSED ); + my->_key = new public_key_data(); + unsigned char* buffer = (unsigned char*) my->_key->begin(); + i2o_ECPublicKey( key, &buffer ); // FIXME: questionable memory handling + EC_KEY_free( key ); + } +} + +public_key::public_key( const public_key_data& dat ) +{ + my->_key = new public_key_data(dat); +} + +public_key::public_key( const compact_signature& c, const fc::sha256& digest, bool check_canonical ) +{ + int nV = c.data[0]; + if (nV<27 || nV>=35) + FC_THROW_EXCEPTION( exception, "unable to reconstruct public key from signature" ); + + if( check_canonical ) + { + FC_ASSERT( is_canonical( c ), "signature is not canonical" ); + } + + my->_key = new public_key_data(); + unsigned int pk_len; + FC_ASSERT( secp256k1_ecdsa_recover_compact( (unsigned char*) digest.data(), (unsigned char*) c.begin() + 1, (unsigned char*) my->_key->begin(), (int*) &pk_len, 1, (*c.begin() - 27) & 3 ) ); + FC_ASSERT( pk_len == my->_key->size() ); +} diff --git a/src/crypto/elliptic_mixed.cpp b/src/crypto/elliptic_mixed.cpp index 8c7e531..8ff3f72 100644 --- a/src/crypto/elliptic_mixed.cpp +++ b/src/crypto/elliptic_mixed.cpp @@ -13,273 +13,15 @@ namespace fc { namespace ecc { namespace detail { - static void init_lib() { - static int init_s = 0; - static int init_o = init_openssl(); - if (!init_s) { - secp256k1_start(SECP256K1_START_VERIFY | SECP256K1_START_SIGN); - init_s = 1; - } - } + static void init_lib(); { typedef public_key_data pub_data_type; typedef EC_KEY priv_data_type; #include "_elliptic_impl.cpp" - - void public_key_impl::free_key() - { - if( _key != nullptr ) - { - delete _key; - _key = nullptr; - } - } - - public_key_data* public_key_impl::dup_key( const public_key_data* cpy ) - { - return new public_key_data( *cpy ); - } - - void public_key_impl::copy_key( public_key_data* to, const public_key_data* from ) - { - *to = *from; - } - - void private_key_impl::free_key() - { - if( _key != nullptr ) - { - EC_KEY_free(_key); - _key = nullptr; - } - } - - EC_KEY* private_key_impl::dup_key( const EC_KEY* cpy ) - { - return EC_KEY_dup( cpy ); - } - - void private_key_impl::copy_key( EC_KEY* to, const EC_KEY* from ) - { - EC_KEY_copy( to, from ); - } } - public_key public_key::add( const fc::sha256& digest )const - { - FC_ASSERT( my->_key != nullptr ); - public_key_data new_key; - memcpy( new_key.begin(), my->_key->begin(), new_key.size() ); - FC_ASSERT( secp256k1_ec_pubkey_tweak_add( (unsigned char*) new_key.begin(), new_key.size(), (unsigned char*) digest.data() ) ); - return public_key( new_key ); - } - - std::string public_key::to_base58() const - { - FC_ASSERT( my->_key != nullptr ); - return to_base58( *my->_key ); - } - - public_key_data public_key::serialize()const - { - FC_ASSERT( my->_key != nullptr ); - return *my->_key; - } - public_key_point_data public_key::serialize_ecc_point()const - { - FC_ASSERT( my->_key != nullptr ); - public_key_point_data dat; - unsigned int pk_len = my->_key->size(); - memcpy( dat.begin(), my->_key->begin(), pk_len ); - FC_ASSERT( secp256k1_ec_pubkey_decompress( (unsigned char *) dat.begin(), (int*) &pk_len ) ); - FC_ASSERT( pk_len == dat.size() ); - return dat; - } - - public_key::public_key( const public_key_point_data& dat ) - { - const char* front = &dat.data[0]; - if( *front == 0 ){} - else - { - EC_KEY *key = EC_KEY_new_by_curve_name( NID_secp256k1 ); - key = o2i_ECPublicKey( &key, (const unsigned char**)&front, sizeof(dat) ); - FC_ASSERT( key ); - EC_KEY_set_conv_form( key, POINT_CONVERSION_COMPRESSED ); - my->_key = new public_key_data(); - unsigned char* buffer = (unsigned char*) my->_key->begin(); - i2o_ECPublicKey( key, &buffer ); // FIXME: questionable memory handling - EC_KEY_free( key ); - } - } - - public_key::public_key( const public_key_data& dat ) - { - my->_key = new public_key_data(dat); - } - - public_key::public_key( const compact_signature& c, const fc::sha256& digest, bool check_canonical ) - { - int nV = c.data[0]; - if (nV<27 || nV>=35) - FC_THROW_EXCEPTION( exception, "unable to reconstruct public key from signature" ); - - if( check_canonical ) - { - FC_ASSERT( is_canonical( c ), "signature is not canonical" ); - } - - my->_key = new public_key_data(); - unsigned int pk_len; - FC_ASSERT( secp256k1_ecdsa_recover_compact( (unsigned char*) digest.data(), (unsigned char*) c.begin() + 1, (unsigned char*) my->_key->begin(), (int*) &pk_len, 1, (*c.begin() - 27) & 3 ) ); - FC_ASSERT( pk_len == my->_key->size() ); - } - - - - static void * ecies_key_derivation(const void *input, size_t ilen, void *output, size_t *olen) - { - if (*olen < SHA512_DIGEST_LENGTH) { - return NULL; - } - *olen = SHA512_DIGEST_LENGTH; - return (void*)SHA512((const unsigned char*)input, ilen, (unsigned char*)output); - } - - // 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 - static int ECDSA_SIG_recover_key_GFp(EC_KEY *eckey, ECDSA_SIG *ecsig, const unsigned char *msg, int msglen, int recid, int check) - { - if (!eckey) FC_THROW_EXCEPTION( exception, "null key" ); - - 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; - } - - int static inline EC_KEY_regenerate_key(EC_KEY *eckey, const 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); - } - - private_key private_key::regenerate( const fc::sha256& secret ) - { - private_key self; - self.my->_key = EC_KEY_new_by_curve_name( NID_secp256k1 ); - if( !self.my->_key ) FC_THROW_EXCEPTION( exception, "Unable to generate EC key" ); - - ssl_bignum bn; - BN_bin2bn( (const unsigned char*)&secret, 32, bn ); - - if( !EC_KEY_regenerate_key(self.my->_key,bn) ) - { - FC_THROW_EXCEPTION( exception, "unable to regenerate key" ); - } - return self; - } - - fc::sha256 private_key::get_secret()const - { - return get_secret( my->_key ); - } - - private_key::private_key( EC_KEY* k ) - { - my->_key = k; - } + #include "_elliptic_mixed_openssl.cpp" public_key private_key::get_public_key()const { @@ -303,79 +45,7 @@ namespace fc { namespace ecc { return buf; } - compact_signature private_key::sign_compact( const fc::sha256& digest )const - { - try { - FC_ASSERT( my->_key != nullptr ); - auto my_pub_key = get_public_key().serialize(); // just for good measure - //ECDSA_SIG *sig = ECDSA_do_sign((unsigned char*)&digest, sizeof(digest), my->_key); - public_key_data key_data; - while( true ) - { - ecdsa_sig sig = ECDSA_do_sign((unsigned char*)&digest, sizeof(digest), my->_key); - - if (sig==nullptr) - FC_THROW_EXCEPTION( exception, "Unable to sign" ); - - compact_signature csig; - // memset( csig.data, 0, sizeof(csig) ); - - int nBitsR = BN_num_bits(sig->r); - int nBitsS = BN_num_bits(sig->s); - if (nBitsR <= 256 && nBitsS <= 256) - { - int nRecId = -1; - EC_KEY* key = EC_KEY_new_by_curve_name( NID_secp256k1 ); - FC_ASSERT( key ); - EC_KEY_set_conv_form( key, POINT_CONVERSION_COMPRESSED ); - for (int i=0; i<4; i++) - { - if (ECDSA_SIG_recover_key_GFp(key, sig, (unsigned char*)&digest, sizeof(digest), i, 1) == 1) - { - unsigned char* buffer = (unsigned char*) key_data.begin(); - i2o_ECPublicKey( key, &buffer ); // FIXME: questionable memory handling - if ( key_data == my_pub_key ) - { - nRecId = i; - break; - } - } - } - EC_KEY_free( key ); - - if (nRecId == -1) - { - FC_THROW_EXCEPTION( exception, "unable to construct recoverable key"); - } - unsigned char* result = nullptr; - auto bytes = i2d_ECDSA_SIG( sig, &result ); - auto lenR = result[3]; - auto lenS = result[5+lenR]; - //idump( (result[0])(result[1])(result[2])(result[3])(result[3+lenR])(result[4+lenR])(bytes)(lenR)(lenS) ); - if( lenR != 32 ) { free(result); continue; } - if( lenS != 32 ) { free(result); continue; } - //idump( (33-(nBitsR+7)/8) ); - //idump( (65-(nBitsS+7)/8) ); - //idump( (sizeof(csig) ) ); - memcpy( &csig.data[1], &result[4], lenR ); - memcpy( &csig.data[33], &result[6+lenR], lenS ); - //idump( (csig.data[33]) ); - //idump( (csig.data[1]) ); - free(result); - //idump( (nRecId) ); - csig.data[0] = nRecId+27+4;//(fCompressedPubKey ? 4 : 0); - /* - idump( (csig) ); - auto rlen = BN_bn2bin(sig->r,&csig.data[33-(nBitsR+7)/8]); - auto slen = BN_bn2bin(sig->s,&csig.data[65-(nBitsS+7)/8]); - idump( (rlen)(slen) ); - */ - } - return csig; - } // while true - } FC_RETHROW_EXCEPTIONS( warn, "sign ${digest}", ("digest", digest)("private_key",*this) ); - } -} -} + #include "_elliptic_mixed_secp256k1.cpp" +} } #include "_elliptic_common.cpp" diff --git a/src/crypto/elliptic_openssl.cpp b/src/crypto/elliptic_openssl.cpp index 414123f..8208ca1 100644 --- a/src/crypto/elliptic_openssl.cpp +++ b/src/crypto/elliptic_openssl.cpp @@ -40,144 +40,9 @@ namespace fc { namespace ecc { { EC_KEY_copy( to, from ); } - - void private_key_impl::free_key() - { - if( _key != nullptr ) - { - EC_KEY_free(_key); - _key = nullptr; - } - } - - EC_KEY* private_key_impl::dup_key( const EC_KEY* cpy ) - { - return EC_KEY_dup( cpy ); - } - - void private_key_impl::copy_key( EC_KEY* to, const EC_KEY* from ) - { - EC_KEY_copy( to, from ); - } } - static void * ecies_key_derivation(const void *input, size_t ilen, void *output, size_t *olen) - { - if (*olen < SHA512_DIGEST_LENGTH) { - return NULL; - } - *olen = SHA512_DIGEST_LENGTH; - return (void*)SHA512((const unsigned char*)input, ilen, (unsigned char*)output); - } - - // 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 - static int ECDSA_SIG_recover_key_GFp(EC_KEY *eckey, ECDSA_SIG *ecsig, const unsigned char *msg, int msglen, int recid, int check) - { - if (!eckey) FC_THROW_EXCEPTION( exception, "null key" ); - - 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; - } - - - int static inline EC_KEY_regenerate_key(EC_KEY *eckey, const 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); - } + #include "_elliptic_mixed_openssl.cpp" /* WARNING! This implementation is broken, it is actually equivalent to * public_key::add()! @@ -256,32 +121,6 @@ namespace fc { namespace ecc { return to_base58( key ); } - private_key private_key::regenerate( const fc::sha256& secret ) - { - private_key self; - self.my->_key = EC_KEY_new_by_curve_name( NID_secp256k1 ); - if( !self.my->_key ) FC_THROW_EXCEPTION( exception, "Unable to generate EC key" ); - - ssl_bignum bn; - BN_bin2bn( (const unsigned char*)&secret, 32, bn ); - - if( !EC_KEY_regenerate_key(self.my->_key,bn) ) - { - FC_THROW_EXCEPTION( exception, "unable to regenerate key" ); - } - return self; - } - - fc::sha256 private_key::get_secret()const - { - return get_secret( my->_key ); - } - - private_key::private_key( EC_KEY* k ) - { - my->_key = k; - } - // signature private_key::sign( const fc::sha256& digest )const // { // unsigned int buf_len = ECDSA_size(my->_key); @@ -415,74 +254,7 @@ namespace fc { namespace ecc { FC_THROW_EXCEPTION( exception, "unable to reconstruct public key from signature" ); } - compact_signature private_key::sign_compact( const fc::sha256& digest )const - { - try { - FC_ASSERT( my->_key != nullptr ); - auto my_pub_key = get_public_key().serialize(); // just for good measure - //ECDSA_SIG *sig = ECDSA_do_sign((unsigned char*)&digest, sizeof(digest), my->_key); - while( true ) - { - ecdsa_sig sig = ECDSA_do_sign((unsigned char*)&digest, sizeof(digest), my->_key); - - if (sig==nullptr) - FC_THROW_EXCEPTION( exception, "Unable to sign" ); - - compact_signature csig; - // memset( csig.data, 0, sizeof(csig) ); - - int nBitsR = BN_num_bits(sig->r); - int nBitsS = BN_num_bits(sig->s); - if (nBitsR <= 256 && nBitsS <= 256) - { - int nRecId = -1; - for (int i=0; i<4; i++) - { - public_key keyRec; - keyRec.my->_key = EC_KEY_new_by_curve_name( NID_secp256k1 ); - if (ECDSA_SIG_recover_key_GFp(keyRec.my->_key, sig, (unsigned char*)&digest, sizeof(digest), i, 1) == 1) - { - if (keyRec.serialize() == my_pub_key ) - { - nRecId = i; - break; - } - } - } - - if (nRecId == -1) - { - FC_THROW_EXCEPTION( exception, "unable to construct recoverable key"); - } - unsigned char* result = nullptr; - auto bytes = i2d_ECDSA_SIG( sig, &result ); - auto lenR = result[3]; - auto lenS = result[5+lenR]; - //idump( (result[0])(result[1])(result[2])(result[3])(result[3+lenR])(result[4+lenR])(bytes)(lenR)(lenS) ); - if( lenR != 32 ) { free(result); continue; } - if( lenS != 32 ) { free(result); continue; } - //idump( (33-(nBitsR+7)/8) ); - //idump( (65-(nBitsS+7)/8) ); - //idump( (sizeof(csig) ) ); - memcpy( &csig.data[1], &result[4], lenR ); - memcpy( &csig.data[33], &result[6+lenR], lenS ); - //idump( (csig.data[33]) ); - //idump( (csig.data[1]) ); - free(result); - //idump( (nRecId) ); - csig.data[0] = nRecId+27+4;//(fCompressedPubKey ? 4 : 0); - /* - idump( (csig) ); - auto rlen = BN_bn2bin(sig->r,&csig.data[33-(nBitsR+7)/8]); - auto slen = BN_bn2bin(sig->s,&csig.data[65-(nBitsS+7)/8]); - idump( (rlen)(slen) ); - */ - } - return csig; - } // while true - } FC_RETHROW_EXCEPTIONS( warn, "sign ${digest}", ("digest", digest)("private_key",*this) ); - } -} -} + #include "_elliptic_mixed_openssl.cpp" +} } #include "_elliptic_common.cpp" diff --git a/src/crypto/elliptic_secp256k1.cpp b/src/crypto/elliptic_secp256k1.cpp index 26f8d3d..e71213c 100644 --- a/src/crypto/elliptic_secp256k1.cpp +++ b/src/crypto/elliptic_secp256k1.cpp @@ -13,39 +13,13 @@ namespace fc { namespace ecc { namespace detail { - static void init_lib() { - static int init_s = 0; - static int init_o = init_openssl(); - if (!init_s) { - secp256k1_start(SECP256K1_START_VERIFY | SECP256K1_START_SIGN); - init_s = 1; - } - } + static void init_lib(); typedef public_key_data pub_data_type; typedef private_key_secret priv_data_type; #include "_elliptic_impl.cpp" - void public_key_impl::free_key() - { - if( _key != nullptr ) - { - delete _key; - _key = nullptr; - } - } - - public_key_data* public_key_impl::dup_key( const public_key_data* cpy ) - { - return new public_key_data( *cpy ); - } - - void public_key_impl::copy_key( public_key_data* to, const public_key_data* from ) - { - *to = *from; - } - void private_key_impl::free_key() { if( _key != nullptr ) @@ -66,21 +40,6 @@ namespace fc { namespace ecc { } } - public_key public_key::add( const fc::sha256& digest )const - { - FC_ASSERT( my->_key != nullptr ); - public_key_data new_key; - memcpy( new_key.begin(), my->_key->begin(), new_key.size() ); - FC_ASSERT( secp256k1_ec_pubkey_tweak_add( (unsigned char*) new_key.begin(), new_key.size(), (unsigned char*) digest.data() ) ); - return public_key( new_key ); - } - - std::string public_key::to_base58() const - { - FC_ASSERT( my->_key != nullptr ); - return to_base58( *my->_key ); - } - private_key private_key::regenerate( const fc::sha256& secret ) { private_key self; @@ -100,44 +59,6 @@ namespace fc { namespace ecc { EC_KEY_free(k); } - public_key_data public_key::serialize()const - { - FC_ASSERT( my->_key != nullptr ); - return *my->_key; - } - public_key_point_data public_key::serialize_ecc_point()const - { - FC_ASSERT( my->_key != nullptr ); - public_key_point_data dat; - unsigned int pk_len = my->_key->size(); - memcpy( dat.begin(), my->_key->begin(), pk_len ); - FC_ASSERT( secp256k1_ec_pubkey_decompress( (unsigned char *) dat.begin(), (int*) &pk_len ) ); - FC_ASSERT( pk_len == dat.size() ); - return dat; - } - - public_key::public_key( const public_key_point_data& dat ) - { - const char* front = &dat.data[0]; - if( *front == 0 ){} - else - { - EC_KEY *key = EC_KEY_new_by_curve_name( NID_secp256k1 ); - key = o2i_ECPublicKey( &key, (const unsigned char**)&front, sizeof(dat) ); - FC_ASSERT( key ); - EC_KEY_set_conv_form( key, POINT_CONVERSION_COMPRESSED ); - my->_key = new public_key_data(); - unsigned char* buffer = (unsigned char*) my->_key->begin(); - i2o_ECPublicKey( key, &buffer ); // FIXME: questionable memory handling - EC_KEY_free( key ); - } - } - - public_key::public_key( const public_key_data& dat ) - { - my->_key = new public_key_data(dat); - } - public_key private_key::get_public_key()const { FC_ASSERT( my->_key != nullptr ); @@ -158,23 +79,6 @@ namespace fc { namespace ecc { return fc::sha512::hash( pub.begin() + 1, pub.size() - 1 ); } - public_key::public_key( const compact_signature& c, const fc::sha256& digest, bool check_canonical ) - { - int nV = c.data[0]; - if (nV<27 || nV>=35) - FC_THROW_EXCEPTION( exception, "unable to reconstruct public key from signature" ); - - if( check_canonical ) - { - FC_ASSERT( is_canonical( c ), "signature is not canonical" ); - } - - my->_key = new public_key_data(); - unsigned int pk_len; - FC_ASSERT( secp256k1_ecdsa_recover_compact( (unsigned char*) digest.data(), (unsigned char*) c.begin() + 1, (unsigned char*) my->_key->begin(), (int*) &pk_len, 1, (*c.begin() - 27) & 3 ) ); - FC_ASSERT( pk_len == my->_key->size() ); - } - compact_signature private_key::sign_compact( const fc::sha256& digest )const { FC_ASSERT( my->_key != nullptr ); @@ -187,7 +91,9 @@ namespace fc { namespace ecc { result.begin()[0] = 27 + 4 + recid; return result; } -} -} + + #include "_elliptic_mixed_secp256k1.cpp" + +} } #include "_elliptic_common.cpp"