Merge pull request #4 from pmconrad/libsecp256k1

fc::ecc implementations based on libsecp256k1
This commit is contained in:
Daniel Larimer 2015-05-20 10:30:05 -04:00
commit ccd14c7260
15 changed files with 1303 additions and 679 deletions

View file

@ -24,6 +24,7 @@ SET( DEFAULT_LIBRARY_INSTALL_DIR lib/ )
SET( DEFAULT_EXECUTABLE_INSTALL_DIR bin/ )
SET( CMAKE_DEBUG_POSTFIX _debug )
SET( BUILD_SHARED_LIBS NO )
SET( ECC_IMPL openssl CACHE STRING "openssl or secp256k1 or mixed" )
set(platformBitness 32)
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
@ -36,6 +37,17 @@ SET(BOOST_COMPONENTS)
LIST(APPEND BOOST_COMPONENTS thread date_time system filesystem program_options signals serialization chrono unit_test_framework context locale iostreams)
SET( Boost_USE_STATIC_LIBS ON CACHE STRING "ON or OFF" )
IF( ECC_IMPL STREQUAL openssl )
SET( ECC_REST src/crypto/elliptic_impl_pub.cpp )
ELSE( ECC_IMPL STREQUAL openssl )
SET( ECC_LIB secp256k1 )
IF( ECC_IMPL STREQUAL mixed )
SET( ECC_REST src/crypto/elliptic_impl_priv.cpp src/crypto/elliptic_impl_pub.cpp )
ELSE( ECC_IMPL STREQUAL mixed )
SET( ECC_REST src/crypto/elliptic_impl_priv.cpp )
ENDIF( ECC_IMPL STREQUAL mixed )
ENDIF( ECC_IMPL STREQUAL openssl )
IF( WIN32 )
MESSAGE(STATUS "Configuring fc to build on Win32")
@ -150,7 +162,9 @@ set( fc_sources
src/crypto/sha512.cpp
src/crypto/dh.cpp
src/crypto/blowfish.cpp
src/crypto/elliptic.cpp
src/crypto/elliptic_common.cpp
${ECC_REST}
src/crypto/elliptic_${ECC_IMPL}.cpp
src/crypto/rand.cpp
src/crypto/salsa20.cpp
#src/crypto/scrypt.cpp
@ -255,8 +269,8 @@ target_include_directories(fc
${CMAKE_CURRENT_SOURCE_DIR}/vendor/websocketpp
)
#target_link_libraries( fc PUBLIC easylzma_static scrypt udt ${Boost_LIBRARIES} ${OPENSSL_LIBRARIES} ${ZLIB_LIBRARIES} ${PLATFORM_SPECIFIC_LIBS} ${RPCRT4} ${CMAKE_DL_LIBS} ${rt_library})
target_link_libraries( fc PUBLIC easylzma_static udt ${Boost_LIBRARIES} ${OPENSSL_LIBRARIES} ${ZLIB_LIBRARIES} ${PLATFORM_SPECIFIC_LIBS} ${RPCRT4} ${CMAKE_DL_LIBS} ${rt_library} ${readline_libraries})
#target_link_libraries( fc PUBLIC easylzma_static scrypt udt ${Boost_LIBRARIES} ${OPENSSL_LIBRARIES} ${ZLIB_LIBRARIES} ${PLATFORM_SPECIFIC_LIBS} ${RPCRT4} ${CMAKE_DL_LIBS} ${rt_library} ${ECC_LIB} )
target_link_libraries( fc PUBLIC easylzma_static udt ${Boost_LIBRARIES} ${OPENSSL_LIBRARIES} ${ZLIB_LIBRARIES} ${PLATFORM_SPECIFIC_LIBS} ${RPCRT4} ${CMAKE_DL_LIBS} ${rt_library} ${readline_libraries} ${ECC_LIB} )
if(MSVC)
set_source_files_properties( src/network/http/websocket.cpp PROPERTIES COMPILE_FLAGS "/bigobj" )

43
README-ecc.md Normal file
View file

@ -0,0 +1,43 @@
ECC Support
===========
include/fc/crypto/elliptic.hpp defines an interface for some cryptographic
wrapper classes handling elliptic curve cryptography.
Three implementations of this interface exist. One is based on OpenSSL, the
others are 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 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 or mixed is chosen, the secp256k1 library and its include file
must already be installed in the appropriate library / include directories on
your system.
Testing
-------
Type "make ecc_test" to build the ecc_test executable from tests/ecc_test.cpp
with the currently configured ECC implementation.
ecc_test expects two arguments:
ecc_test <pass> <interop-file>
<pass> is a somewhat arbitrary password used for testing.
<interop-file> is a data file containing intermediate test results.
If the file does not exist, it will be created and intermediate results from
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 can use the script
tests/ecc-interop.sh .
None of the test runs should produce any output.

View file

@ -1,5 +1,6 @@
#pragma once
#include <fc/crypto/bigint.hpp>
#include <fc/crypto/openssl.hpp>
#include <fc/crypto/sha256.hpp>
#include <fc/crypto/sha512.hpp>
#include <fc/fwd.hpp>
@ -31,7 +32,7 @@ namespace fc {
public_key();
public_key(const public_key& k);
~public_key();
bool verify( const fc::sha256& digest, const signature& sig );
// bool verify( const fc::sha256& digest, const signature& sig );
public_key_data serialize()const;
public_key_point_data serialize_ecc_point()const;
@ -43,7 +44,10 @@ namespace fc {
public_key( const compact_signature& c, const fc::sha256& digest, bool check_canonical = true );
bool valid()const;
public_key mult( const fc::sha256& offset );
/** Computes new pubkey = generator * offset + old pubkey ?! */
// public_key mult( const fc::sha256& offset )const;
/** Computes new pubkey = regenerate(offset).pubkey + old pubkey
* = offset * G + 1 * old pubkey ?! */
public_key add( const fc::sha256& offset )const;
public_key( public_key&& pk );
@ -61,11 +65,14 @@ namespace fc {
/// Allows to convert current public key object into base58 number.
std::string to_base58() const;
static std::string to_base58( const public_key_data &key );
static public_key from_base58( const std::string& b58 );
private:
friend class private_key;
fc::fwd<detail::public_key_impl,8> my;
static public_key from_key_data( const public_key_data& v );
static bool is_canonical( const compact_signature& c );
fc::fwd<detail::public_key_impl,33> my;
};
/**
@ -103,9 +110,9 @@ namespace fc {
*/
fc::sha512 get_shared_secret( const public_key& pub )const;
signature sign( const fc::sha256& digest )const;
// signature sign( const fc::sha256& digest )const;
compact_signature sign_compact( const fc::sha256& digest )const;
bool verify( const fc::sha256& digest, const signature& sig );
// bool verify( const fc::sha256& digest, const signature& sig );
public_key get_public_key()const;
@ -123,7 +130,9 @@ namespace fc {
}
private:
fc::fwd<detail::private_key_impl,8> my;
private_key( EC_KEY* k );
static fc::sha256 get_secret( const EC_KEY * const k );
fc::fwd<detail::private_key_impl,32> my;
};
} // namespace ecc
void to_variant( const ecc::private_key& var, variant& vo );

View file

@ -0,0 +1,25 @@
#pragma once
#include <fc/crypto/elliptic.hpp>
/* private_key_impl based on libsecp256k1
* used by mixed + secp256k1
*/
namespace fc { namespace ecc { namespace detail {
const secp256k1_context_t* _get_context();
void _init_lib();
class private_key_impl
{
public:
private_key_impl() noexcept;
private_key_impl( const private_key_impl& cpy ) noexcept;
private_key_impl& operator=( const private_key_impl& pk ) noexcept;
private_key_secret _key;
};
}}}

View file

@ -0,0 +1,32 @@
#pragma once
#include <fc/crypto/elliptic.hpp>
/* public_key_impl implementation based on openssl
* used by mixed + openssl
*/
namespace fc { namespace ecc { namespace detail {
void _init_lib();
class public_key_impl
{
public:
public_key_impl() noexcept;
public_key_impl( const public_key_impl& cpy ) noexcept;
public_key_impl( public_key_impl&& cpy ) noexcept;
~public_key_impl() noexcept;
public_key_impl& operator=( const public_key_impl& pk ) noexcept;
public_key_impl& operator=( public_key_impl&& pk ) noexcept;
static int ECDSA_SIG_recover_key_GFp(EC_KEY *eckey, ECDSA_SIG *ecsig, const unsigned char *msg, int msglen, int recid, int check);
EC_KEY* _key = nullptr;
private:
void free_key() noexcept;
};
}}}

View file

@ -1,668 +0,0 @@
#include <fc/crypto/elliptic.hpp>
#include <fc/crypto/base58.hpp>
#include <fc/crypto/openssl.hpp>
#include <fc/fwd_impl.hpp>
#include <fc/exception/exception.hpp>
#include <fc/log/logger.hpp>
#include <assert.h>
namespace fc { namespace ecc {
namespace detail
{
class public_key_impl
{
public:
public_key_impl()
:_key(nullptr)
{
static int init = init_openssl();
}
~public_key_impl()
{
if( _key != nullptr )
{
EC_KEY_free(_key);
}
}
public_key_impl( const public_key_impl& cpy )
{
_key = cpy._key ? EC_KEY_dup( cpy._key ) : nullptr;
}
EC_KEY* _key;
};
class private_key_impl
{
public:
private_key_impl()
:_key(nullptr)
{
static int init = init_openssl();
}
~private_key_impl()
{
if( _key != nullptr )
{
EC_KEY_free(_key);
}
}
private_key_impl( const private_key_impl& cpy )
{
_key = cpy._key ? EC_KEY_dup( cpy._key ) : nullptr;
}
EC_KEY* _key;
};
}
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
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);
}
/*
public_key::public_key()
:my( new detail::public_key_impl() )
{
}
public_key::public_key( fc::bigint pub_x, fc::bigint pub_y )
:my( new detail::public_key_impl() )
{
}
public_key::~public_key()
{
}
*/
public_key public_key::mult( const fc::sha256& digest )
{
// get point from this public key
const EC_POINT* master_pub = EC_KEY_get0_public_key( my->_key );
ec_group group(EC_GROUP_new_by_curve_name(NID_secp256k1));
ssl_bignum z;
BN_bin2bn((unsigned char*)&digest, sizeof(digest), z);
// multiply by digest
ssl_bignum one;
BN_one(one);
bn_ctx ctx(BN_CTX_new());
ec_point result(EC_POINT_new(group));
EC_POINT_mul(group, result, z, master_pub, one, ctx);
public_key rtn;
rtn.my->_key = EC_KEY_new_by_curve_name( NID_secp256k1 );
EC_KEY_set_public_key(rtn.my->_key,result);
return rtn;
}
bool public_key::valid()const
{
return my->_key != nullptr;
}
public_key public_key::add( const fc::sha256& digest )const
{
try {
ec_group group(EC_GROUP_new_by_curve_name(NID_secp256k1));
bn_ctx ctx(BN_CTX_new());
fc::bigint digest_bi( (char*)&digest, sizeof(digest) );
ssl_bignum order;
EC_GROUP_get_order(group, order, ctx);
if( digest_bi > fc::bigint(order) )
{
FC_THROW_EXCEPTION( exception, "digest > group order" );
}
public_key digest_key = private_key::regenerate(digest).get_public_key();
const EC_POINT* digest_point = EC_KEY_get0_public_key( digest_key.my->_key );
// get point from this public key
const EC_POINT* master_pub = EC_KEY_get0_public_key( my->_key );
ssl_bignum z;
BN_bin2bn((unsigned char*)&digest, sizeof(digest), z);
// multiply by digest
ssl_bignum one;
BN_one(one);
ec_point result(EC_POINT_new(group));
EC_POINT_add(group, result, digest_point, master_pub, ctx);
if (EC_POINT_is_at_infinity(group, result))
{
FC_THROW_EXCEPTION( exception, "point at infinity" );
}
public_key rtn;
rtn.my->_key = EC_KEY_new_by_curve_name( NID_secp256k1 );
EC_KEY_set_public_key(rtn.my->_key,result);
return rtn;
} FC_RETHROW_EXCEPTIONS( debug, "digest: ${digest}", ("digest",digest) );
}
std::string public_key::to_base58() const
{
public_key_data key = serialize();
uint32_t check = (uint32_t)sha256::hash(key.data, sizeof(key))._hash[0];
assert(key.size() + sizeof(check) == 37);
array<char, 37> data;
memcpy(data.data, key.begin(), key.size());
memcpy(data.begin() + key.size(), (const char*)&check, sizeof(check));
return fc::to_base58(data.begin(), data.size());
}
public_key public_key::from_base58( const std::string& b58 )
{
array<char, 37> data;
size_t s = fc::from_base58(b58, (char*)&data, sizeof(data) );
FC_ASSERT( s == sizeof(data) );
public_key_data key;
uint32_t check = (uint32_t)sha256::hash(data.data, sizeof(key))._hash[0];
FC_ASSERT( memcmp( (char*)&check, data.data + sizeof(key), sizeof(check) ) == 0 );
memcpy( (char*)key.data, data.data, sizeof(key) );
return public_key(key);
}
private_key::private_key()
{}
private_key private_key::generate_from_seed( const fc::sha256& seed, const fc::sha256& offset )
{
ssl_bignum z;
BN_bin2bn((unsigned char*)&offset, sizeof(offset), z);
ec_group group(EC_GROUP_new_by_curve_name(NID_secp256k1));
bn_ctx ctx(BN_CTX_new());
ssl_bignum order;
EC_GROUP_get_order(group, order, ctx);
// secexp = (seed + z) % order
ssl_bignum secexp;
BN_bin2bn((unsigned char*)&seed, sizeof(seed), secexp);
BN_add(secexp, secexp, z);
BN_mod(secexp, secexp, order, ctx);
fc::sha256 secret;
assert(BN_num_bytes(secexp) <= int64_t(sizeof(secret)));
auto shift = sizeof(secret) - BN_num_bytes(secexp);
BN_bn2bin(secexp, ((unsigned char*)&secret)+shift);
return regenerate( secret );
}
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
{
if( !my->_key )
{
return fc::sha256();
}
fc::sha256 sec;
const BIGNUM* bn = EC_KEY_get0_private_key(my->_key);
if( bn == NULL )
{
FC_THROW_EXCEPTION( exception, "get private key failed" );
}
int nbytes = BN_num_bytes(bn);
BN_bn2bin(bn, &((unsigned char*)&sec)[32-nbytes] );
return sec;
}
private_key private_key::generate()
{
private_key self;
EC_KEY* k = EC_KEY_new_by_curve_name( NID_secp256k1 );
if( !k ) FC_THROW_EXCEPTION( exception, "Unable to generate EC key" );
self.my->_key = k;
if( !EC_KEY_generate_key( self.my->_key ) )
{
FC_THROW_EXCEPTION( exception, "ecc key generation error" );
}
#if 0
= bigint( EC_KEY_get0_private_key( k );
EC_POINT* pub = EC_KEY_get0_public_key( k );
EC_GROUP* group = EC_KEY_get0_group( k );
EC_POINT_get_affine_coordinates_GFp( group, pub, self.my->_pub_x.get(), self.my->_pub_y.get(), nullptr/*ctx*/ );
EC_KEY_free(k);
#endif
return self;
}
signature private_key::sign( const fc::sha256& digest )const
{
unsigned int buf_len = ECDSA_size(my->_key);
// fprintf( stderr, "%d %d\n", buf_len, sizeof(sha256) );
signature sig;
assert( buf_len == sizeof(sig) );
if( !ECDSA_sign( 0,
(const unsigned char*)&digest, sizeof(digest),
(unsigned char*)&sig, &buf_len, my->_key ) )
{
FC_THROW_EXCEPTION( exception, "signing error" );
}
return sig;
}
bool public_key::verify( const fc::sha256& digest, const fc::ecc::signature& sig )
{
return 1 == ECDSA_verify( 0, (unsigned char*)&digest, sizeof(digest), (unsigned char*)&sig, sizeof(sig), my->_key );
}
public_key_data public_key::serialize()const
{
public_key_data dat;
if( !my->_key ) return dat;
EC_KEY_set_conv_form( my->_key, POINT_CONVERSION_COMPRESSED );
/*size_t nbytes = i2o_ECPublicKey( my->_key, nullptr ); */
/*assert( nbytes == 33 )*/
char* front = &dat.data[0];
i2o_ECPublicKey( my->_key, (unsigned char**)&front );
return dat;
/*
EC_POINT* pub = EC_KEY_get0_public_key( my->_key );
EC_GROUP* group = EC_KEY_get0_group( my->_key );
EC_POINT_get_affine_coordinates_GFp( group, pub, self.my->_pub_x.get(), self.my->_pub_y.get(), nullptr );
*/
}
public_key_point_data public_key::serialize_ecc_point()const
{
public_key_point_data dat;
if( !my->_key ) return dat;
EC_KEY_set_conv_form( my->_key, POINT_CONVERSION_UNCOMPRESSED );
char* front = &dat.data[0];
i2o_ECPublicKey( my->_key, (unsigned char**)&front );
return dat;
}
public_key::public_key()
{
}
public_key::~public_key()
{
}
public_key::public_key( const public_key_point_data& dat )
{
const char* front = &dat.data[0];
if( *front == 0 ){}
else
{
/*my->_key = EC_KEY_new_by_curve_name( NID_secp256k1 ); */
my->_key = o2i_ECPublicKey( &my->_key, (const unsigned char**)&front, sizeof(dat) );
if( !my->_key )
{
FC_THROW_EXCEPTION( exception, "error decoding public key", ("s", ERR_error_string( ERR_get_error(), nullptr) ) );
}
}
}
public_key::public_key( const public_key_data& dat )
{
const char* front = &dat.data[0];
if( *front == 0 ){}
else
{
my->_key = EC_KEY_new_by_curve_name( NID_secp256k1 );
my->_key = o2i_ECPublicKey( &my->_key, (const unsigned char**)&front, sizeof(public_key_data) );
if( !my->_key )
{
FC_THROW_EXCEPTION( exception, "error decoding public key", ("s", ERR_error_string( ERR_get_error(), nullptr) ) );
}
}
}
bool private_key::verify( const fc::sha256& digest, const fc::ecc::signature& sig )
{
return 1 == ECDSA_verify( 0, (unsigned char*)&digest, sizeof(digest), (unsigned char*)&sig, sizeof(sig), my->_key );
}
public_key private_key::get_public_key()const
{
public_key pub;
pub.my->_key = EC_KEY_new_by_curve_name( NID_secp256k1 );
EC_KEY_set_public_key( pub.my->_key, EC_KEY_get0_public_key( my->_key ) );
return pub;
}
fc::sha512 private_key::get_shared_secret( const public_key& other )const
{
FC_ASSERT( my->_key != nullptr );
FC_ASSERT( other.my->_key != nullptr );
fc::sha512 buf;
ECDH_compute_key( (unsigned char*)&buf, sizeof(buf), EC_KEY_get0_public_key(other.my->_key), my->_key, ecies_key_derivation );
return buf;
}
private_key::~private_key()
{
}
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" );
ECDSA_SIG *sig = ECDSA_SIG_new();
BN_bin2bn(&c.data[1],32,sig->r);
BN_bin2bn(&c.data[33],32,sig->s);
if( check_canonical )
{
FC_ASSERT( !(c.data[1] & 0x80), "signature is not canonical" );
FC_ASSERT( !(c.data[1] == 0 && !(c.data[2] & 0x80)), "signature is not canonical" );
FC_ASSERT( !(c.data[33] & 0x80), "signature is not canonical" );
FC_ASSERT( !(c.data[33] == 0 && !(c.data[34] & 0x80)), "signature is not canonical" );
}
my->_key = EC_KEY_new_by_curve_name(NID_secp256k1);
if (nV >= 31)
{
EC_KEY_set_conv_form( my->_key, POINT_CONVERSION_COMPRESSED );
nV -= 4;
// fprintf( stderr, "compressed\n" );
}
if (ECDSA_SIG_recover_key_GFp(my->_key, sig, (unsigned char*)&digest, sizeof(digest), nV - 27, 0) == 1)
{
ECDSA_SIG_free(sig);
return;
}
ECDSA_SIG_free(sig);
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) );
}
private_key& private_key::operator=( private_key&& pk )
{
if( my->_key )
{
EC_KEY_free(my->_key);
}
my->_key = pk.my->_key;
pk.my->_key = nullptr;
return *this;
}
public_key::public_key( const public_key& pk )
:my(pk.my)
{
}
public_key::public_key( public_key&& pk )
:my( fc::move( pk.my) )
{
}
private_key::private_key( const private_key& pk )
:my(pk.my)
{
}
private_key::private_key( private_key&& pk )
:my( fc::move( pk.my) )
{
}
public_key& public_key::operator=( public_key&& pk )
{
if( my->_key )
{
EC_KEY_free(my->_key);
}
my->_key = pk.my->_key;
pk.my->_key = nullptr;
return *this;
}
public_key& public_key::operator=( const public_key& pk )
{
if( my->_key )
{
EC_KEY_free(my->_key);
}
my->_key = EC_KEY_dup(pk.my->_key);
return *this;
}
private_key& private_key::operator=( const private_key& pk )
{
if( my->_key )
{
EC_KEY_free(my->_key);
}
my->_key = EC_KEY_dup(pk.my->_key);
return *this;
}
}
void to_variant( const ecc::private_key& var, variant& vo )
{
vo = var.get_secret();
}
void from_variant( const variant& var, ecc::private_key& vo )
{
fc::sha256 sec;
from_variant( var, sec );
vo = ecc::private_key::regenerate(sec);
}
void to_variant( const ecc::public_key& var, variant& vo )
{
vo = var.serialize();
}
void from_variant( const variant& var, ecc::public_key& vo )
{
ecc::public_key_data dat;
from_variant( var, dat );
vo = ecc::public_key(dat);
}
}

View file

@ -0,0 +1,122 @@
#include <fc/crypto/base58.hpp>
#include <fc/crypto/elliptic.hpp>
/* stuff common to all ecc implementations */
namespace fc { namespace ecc {
public_key public_key::from_key_data( const public_key_data &data ) {
return public_key(data);
}
std::string public_key::to_base58( const public_key_data &key )
{
uint32_t check = (uint32_t)sha256::hash(key.data, sizeof(key))._hash[0];
assert(key.size() + sizeof(check) == 37);
array<char, 37> data;
memcpy(data.data, key.begin(), key.size());
memcpy(data.begin() + key.size(), (const char*)&check, sizeof(check));
return fc::to_base58(data.begin(), data.size());
}
public_key public_key::from_base58( const std::string& b58 )
{
array<char, 37> data;
size_t s = fc::from_base58(b58, (char*)&data, sizeof(data) );
FC_ASSERT( s == sizeof(data) );
public_key_data key;
uint32_t check = (uint32_t)sha256::hash(data.data, sizeof(key))._hash[0];
FC_ASSERT( memcmp( (char*)&check, data.data + sizeof(key), sizeof(check) ) == 0 );
memcpy( (char*)key.data, data.data, sizeof(key) );
return from_key_data(key);
}
bool public_key::is_canonical( const compact_signature& c ) {
return !(c.data[1] & 0x80)
&& !(c.data[1] == 0 && !(c.data[2] & 0x80))
&& !(c.data[33] & 0x80)
&& !(c.data[33] == 0 && !(c.data[34] & 0x80));
}
private_key private_key::generate_from_seed( const fc::sha256& seed, const fc::sha256& offset )
{
ssl_bignum z;
BN_bin2bn((unsigned char*)&offset, sizeof(offset), z);
ec_group group(EC_GROUP_new_by_curve_name(NID_secp256k1));
bn_ctx ctx(BN_CTX_new());
ssl_bignum order;
EC_GROUP_get_order(group, order, ctx);
// secexp = (seed + z) % order
ssl_bignum secexp;
BN_bin2bn((unsigned char*)&seed, sizeof(seed), secexp);
BN_add(secexp, secexp, z);
BN_mod(secexp, secexp, order, ctx);
fc::sha256 secret;
assert(BN_num_bytes(secexp) <= int64_t(sizeof(secret)));
auto shift = sizeof(secret) - BN_num_bytes(secexp);
BN_bn2bin(secexp, ((unsigned char*)&secret)+shift);
return regenerate( secret );
}
fc::sha256 private_key::get_secret( const EC_KEY * const k )
{
if( !k )
{
return fc::sha256();
}
fc::sha256 sec;
const BIGNUM* bn = EC_KEY_get0_private_key(k);
if( bn == NULL )
{
FC_THROW_EXCEPTION( exception, "get private key failed" );
}
int nbytes = BN_num_bytes(bn);
BN_bn2bin(bn, &((unsigned char*)&sec)[32-nbytes] );
return sec;
}
private_key private_key::generate()
{
EC_KEY* k = EC_KEY_new_by_curve_name( NID_secp256k1 );
if( !k ) FC_THROW_EXCEPTION( exception, "Unable to generate EC key" );
if( !EC_KEY_generate_key( k ) )
{
FC_THROW_EXCEPTION( exception, "ecc key generation error" );
}
return private_key( k );
}
}
void to_variant( const ecc::private_key& var, variant& vo )
{
vo = var.get_secret();
}
void from_variant( const variant& var, ecc::private_key& vo )
{
fc::sha256 sec;
from_variant( var, sec );
vo = ecc::private_key::regenerate(sec);
}
void to_variant( const ecc::public_key& var, variant& vo )
{
vo = var.serialize();
}
void from_variant( const variant& var, ecc::public_key& vo )
{
ecc::public_key_data dat;
from_variant( var, dat );
vo = ecc::public_key(dat);
}
}

View file

@ -0,0 +1,102 @@
#include <fc/fwd_impl.hpp>
#include <secp256k1.h>
#include "_elliptic_impl_priv.hpp"
/* used by mixed + secp256k1 */
namespace fc { namespace ecc {
namespace detail {
private_key_impl::private_key_impl() noexcept
{
_init_lib();
}
private_key_impl::private_key_impl( const private_key_impl& cpy ) noexcept
{
_init_lib();
this->_key = cpy._key;
}
private_key_impl& private_key_impl::operator=( const private_key_impl& pk ) noexcept
{
_key = pk._key;
return *this;
}
}
static const private_key_secret empty_priv;
private_key::private_key() {}
private_key::private_key( const private_key& pk ) : my( pk.my ) {}
private_key::private_key( private_key&& pk ) : my( std::move( pk.my ) ) {}
private_key::~private_key() {}
private_key& private_key::operator=( private_key&& pk )
{
my = std::move( pk.my );
return *this;
}
private_key& private_key::operator=( const private_key& pk )
{
my = pk.my;
return *this;
}
private_key private_key::regenerate( const fc::sha256& secret )
{
private_key self;
self.my->_key = secret;
return self;
}
fc::sha256 private_key::get_secret()const
{
return my->_key;
}
private_key::private_key( EC_KEY* k )
{
my->_key = get_secret( k );
EC_KEY_free(k);
}
public_key private_key::get_public_key()const
{
FC_ASSERT( my->_key != empty_priv );
public_key_data pub;
unsigned int pk_len;
FC_ASSERT( secp256k1_ec_pubkey_create( detail::_get_context(), (unsigned char*) pub.begin(), (int*) &pk_len, (unsigned char*) my->_key.data(), 1 ) );
FC_ASSERT( pk_len == pub.size() );
return public_key(pub);
}
static int extended_nonce_function( unsigned char *nonce32, const unsigned char *msg32,
const unsigned char *key32, unsigned int attempt,
const void *data ) {
unsigned int* extra = (unsigned int*) data;
(*extra)++;
return secp256k1_nonce_function_default( nonce32, msg32, key32, *extra, nullptr );
}
compact_signature private_key::sign_compact( const fc::sha256& digest )const
{
FC_ASSERT( my->_key != empty_priv );
compact_signature result;
int recid;
unsigned int counter = 0;
do
{
FC_ASSERT( secp256k1_ecdsa_sign_compact( detail::_get_context(), (unsigned char*) digest.data(), (unsigned char*) result.begin() + 1, (unsigned char*) my->_key.data(), extended_nonce_function, &counter, &recid ));
} while( !public_key::is_canonical( result ) );
result.begin()[0] = 27 + 4 + recid;
return result;
}
}}

View file

@ -0,0 +1,357 @@
#include <fc/fwd_impl.hpp>
#include "_elliptic_impl_pub.hpp"
/* used by mixed + openssl */
namespace fc { namespace ecc {
namespace detail {
public_key_impl::public_key_impl() noexcept
{
_init_lib();
}
public_key_impl::public_key_impl( const public_key_impl& cpy ) noexcept
{
_init_lib();
*this = cpy;
}
public_key_impl::public_key_impl( public_key_impl&& cpy ) noexcept
{
_init_lib();
*this = cpy;
}
public_key_impl::~public_key_impl() noexcept
{
free_key();
}
public_key_impl& public_key_impl::operator=( const public_key_impl& pk ) noexcept
{
if (pk._key == nullptr)
{
free_key();
} else if ( _key == nullptr ) {
_key = EC_KEY_dup( pk._key );
} else {
EC_KEY_copy( _key, pk._key );
}
return *this;
}
public_key_impl& public_key_impl::operator=( public_key_impl&& pk ) noexcept
{
if ( this != &pk ) {
free_key();
_key = pk._key;
pk._key = nullptr;
}
return *this;
}
void public_key_impl::free_key() noexcept
{
if( _key != nullptr )
{
EC_KEY_free(_key);
_key = nullptr;
}
}
// 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 public_key_impl::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;
}
}
public_key::public_key() {}
public_key::public_key( const public_key& pk ) : my( pk.my ) {}
public_key::public_key( public_key&& pk ) : my( std::move( pk.my ) ) {}
public_key::~public_key() {}
public_key& public_key::operator=( public_key&& pk )
{
my = std::move(pk.my);
return *this;
}
public_key& public_key::operator=( const public_key& pk )
{
my = pk.my;
return *this;
}
bool public_key::valid()const
{
return my->_key != nullptr;
}
/* WARNING! This implementation is broken, it is actually equivalent to
* public_key::add()!
*/
// public_key public_key::mult( const fc::sha256& digest ) const
// {
// // get point from this public key
// const EC_POINT* master_pub = EC_KEY_get0_public_key( my->_key );
// ec_group group(EC_GROUP_new_by_curve_name(NID_secp256k1));
//
// ssl_bignum z;
// BN_bin2bn((unsigned char*)&digest, sizeof(digest), z);
//
// // multiply by digest
// ssl_bignum one;
// BN_one(one);
// bn_ctx ctx(BN_CTX_new());
//
// ec_point result(EC_POINT_new(group));
// EC_POINT_mul(group, result, z, master_pub, one, ctx);
//
// public_key rtn;
// rtn.my->_key = EC_KEY_new_by_curve_name( NID_secp256k1 );
// EC_KEY_set_public_key(rtn.my->_key,result);
//
// return rtn;
// }
public_key public_key::add( const fc::sha256& digest )const
{
try {
ec_group group(EC_GROUP_new_by_curve_name(NID_secp256k1));
bn_ctx ctx(BN_CTX_new());
fc::bigint digest_bi( (char*)&digest, sizeof(digest) );
ssl_bignum order;
EC_GROUP_get_order(group, order, ctx);
if( digest_bi > fc::bigint(order) )
{
FC_THROW_EXCEPTION( exception, "digest > group order" );
}
public_key digest_key = private_key::regenerate(digest).get_public_key();
const EC_POINT* digest_point = EC_KEY_get0_public_key( digest_key.my->_key );
// get point from this public key
const EC_POINT* master_pub = EC_KEY_get0_public_key( my->_key );
// ssl_bignum z;
// BN_bin2bn((unsigned char*)&digest, sizeof(digest), z);
// multiply by digest
// ssl_bignum one;
// BN_one(one);
ec_point result(EC_POINT_new(group));
EC_POINT_add(group, result, digest_point, master_pub, ctx);
if (EC_POINT_is_at_infinity(group, result))
{
FC_THROW_EXCEPTION( exception, "point at infinity" );
}
public_key rtn;
rtn.my->_key = EC_KEY_new_by_curve_name( NID_secp256k1 );
EC_KEY_set_public_key(rtn.my->_key,result);
return rtn;
} FC_RETHROW_EXCEPTIONS( debug, "digest: ${digest}", ("digest",digest) );
}
std::string public_key::to_base58() const
{
public_key_data key = serialize();
return to_base58( key );
}
// signature private_key::sign( const fc::sha256& digest )const
// {
// unsigned int buf_len = ECDSA_size(my->_key);
//// fprintf( stderr, "%d %d\n", buf_len, sizeof(sha256) );
// signature sig;
// assert( buf_len == sizeof(sig) );
//
// if( !ECDSA_sign( 0,
// (const unsigned char*)&digest, sizeof(digest),
// (unsigned char*)&sig, &buf_len, my->_key ) )
// {
// FC_THROW_EXCEPTION( exception, "signing error" );
// }
//
//
// return sig;
// }
// bool public_key::verify( const fc::sha256& digest, const fc::ecc::signature& sig )
// {
// return 1 == ECDSA_verify( 0, (unsigned char*)&digest, sizeof(digest), (unsigned char*)&sig, sizeof(sig), my->_key );
// }
public_key_data public_key::serialize()const
{
public_key_data dat;
if( !my->_key ) return dat;
EC_KEY_set_conv_form( my->_key, POINT_CONVERSION_COMPRESSED );
/*size_t nbytes = i2o_ECPublicKey( my->_key, nullptr ); */
/*assert( nbytes == 33 )*/
char* front = &dat.data[0];
i2o_ECPublicKey( my->_key, (unsigned char**)&front ); // FIXME: questionable memory handling
return dat;
/*
EC_POINT* pub = EC_KEY_get0_public_key( my->_key );
EC_GROUP* group = EC_KEY_get0_group( my->_key );
EC_POINT_get_affine_coordinates_GFp( group, pub, self.my->_pub_x.get(), self.my->_pub_y.get(), nullptr );
*/
}
public_key_point_data public_key::serialize_ecc_point()const
{
public_key_point_data dat;
if( !my->_key ) return dat;
EC_KEY_set_conv_form( my->_key, POINT_CONVERSION_UNCOMPRESSED );
char* front = &dat.data[0];
i2o_ECPublicKey( my->_key, (unsigned char**)&front ); // FIXME: questionable memory handling
return dat;
}
public_key::public_key( const public_key_point_data& dat )
{
const char* front = &dat.data[0];
if( *front == 0 ){}
else
{
my->_key = EC_KEY_new_by_curve_name( NID_secp256k1 );
my->_key = o2i_ECPublicKey( &my->_key, (const unsigned char**)&front, sizeof(dat) );
if( !my->_key )
{
FC_THROW_EXCEPTION( exception, "error decoding public key", ("s", ERR_error_string( ERR_get_error(), nullptr) ) );
}
}
}
public_key::public_key( const public_key_data& dat )
{
const char* front = &dat.data[0];
if( *front == 0 ){}
else
{
my->_key = EC_KEY_new_by_curve_name( NID_secp256k1 );
my->_key = o2i_ECPublicKey( &my->_key, (const unsigned char**)&front, sizeof(public_key_data) );
if( !my->_key )
{
FC_THROW_EXCEPTION( exception, "error decoding public key", ("s", ERR_error_string( ERR_get_error(), nullptr) ) );
}
}
}
// bool private_key::verify( const fc::sha256& digest, const fc::ecc::signature& sig )
// {
// return 1 == ECDSA_verify( 0, (unsigned char*)&digest, sizeof(digest), (unsigned char*)&sig, sizeof(sig), my->_key );
// }
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" );
ECDSA_SIG *sig = ECDSA_SIG_new();
BN_bin2bn(&c.data[1],32,sig->r);
BN_bin2bn(&c.data[33],32,sig->s);
if( check_canonical )
{
FC_ASSERT( is_canonical( c ), "signature is not canonical" );
}
my->_key = EC_KEY_new_by_curve_name(NID_secp256k1);
if (nV >= 31)
{
EC_KEY_set_conv_form( my->_key, POINT_CONVERSION_COMPRESSED );
nV -= 4;
// fprintf( stderr, "compressed\n" );
}
if (detail::public_key_impl::ECDSA_SIG_recover_key_GFp(my->_key, sig, (unsigned char*)&digest, sizeof(digest), nV - 27, 0) == 1)
{
ECDSA_SIG_free(sig);
return;
}
ECDSA_SIG_free(sig);
FC_THROW_EXCEPTION( exception, "unable to reconstruct public key from signature" );
}
}}

View file

@ -0,0 +1,40 @@
#include <fc/crypto/elliptic.hpp>
#include <fc/crypto/base58.hpp>
#include <fc/crypto/openssl.hpp>
#include <fc/fwd_impl.hpp>
#include <fc/exception/exception.hpp>
#include <fc/log/logger.hpp>
#include <assert.h>
#include <secp256k1.h>
#include "_elliptic_impl_priv.hpp"
#include "_elliptic_impl_pub.hpp"
namespace fc { namespace ecc {
namespace detail
{
const secp256k1_context_t* _get_context() {
static secp256k1_context_t* ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN);
return ctx;
}
void _init_lib() {
static const secp256k1_context_t* ctx = _get_context();
static int init_o = init_openssl();
}
}
static const private_key_secret empty_priv;
fc::sha512 private_key::get_shared_secret( const public_key& other )const
{
FC_ASSERT( my->_key != empty_priv );
FC_ASSERT( other.my->_key != nullptr );
public_key_data pub(other.serialize());
FC_ASSERT( secp256k1_ec_pubkey_tweak_mul( detail::_get_context(), (unsigned char*) pub.begin(), pub.size(), (unsigned char*) my->_key.data() ) );
return fc::sha512::hash( pub.begin() + 1, pub.size() - 1 );
}
} }

View file

@ -0,0 +1,261 @@
#include <fc/crypto/elliptic.hpp>
#include <fc/crypto/base58.hpp>
#include <fc/crypto/openssl.hpp>
#include <fc/fwd_impl.hpp>
#include <fc/exception/exception.hpp>
#include <fc/log/logger.hpp>
#include <assert.h>
#include "_elliptic_impl_pub.hpp"
namespace fc { namespace ecc {
namespace detail
{
void _init_lib() {
static int init_o = init_openssl();
}
class private_key_impl
{
public:
private_key_impl() noexcept
{
_init_lib();
}
private_key_impl( const private_key_impl& cpy ) noexcept
{
_init_lib();
*this = cpy;
}
private_key_impl( private_key_impl&& cpy ) noexcept
{
_init_lib();
*this = cpy;
}
~private_key_impl() noexcept
{
free_key();
}
private_key_impl& operator=( const private_key_impl& pk ) noexcept
{
if (pk._key == nullptr)
{
free_key();
} else if ( _key == nullptr ) {
_key = EC_KEY_dup( pk._key );
} else {
EC_KEY_copy( _key, pk._key );
}
return *this;
}
private_key_impl& operator=( private_key_impl&& pk ) noexcept
{
if ( this != &pk ) {
free_key();
_key = pk._key;
pk._key = nullptr;
}
return *this;
}
EC_KEY* _key = nullptr;
private:
void free_key() noexcept
{
if( _key != nullptr )
{
EC_KEY_free(_key);
_key = nullptr;
}
}
};
}
private_key::private_key() {}
private_key::private_key( const private_key& pk ) : my( pk.my ) {}
private_key::private_key( private_key&& pk ) : my( std::move( pk.my ) ) {}
private_key::~private_key() {}
private_key& private_key::operator=( private_key&& pk )
{
my = std::move(pk.my);
return *this;
}
private_key& private_key::operator=( const private_key& pk )
{
my = pk.my;
return *this;
}
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);
}
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;
}
public_key private_key::get_public_key()const
{
public_key pub;
pub.my->_key = EC_KEY_new_by_curve_name( NID_secp256k1 );
EC_KEY_set_public_key( pub.my->_key, EC_KEY_get0_public_key( my->_key ) );
return pub;
}
fc::sha512 private_key::get_shared_secret( const public_key& other )const
{
FC_ASSERT( my->_key != nullptr );
FC_ASSERT( other.my->_key != nullptr );
fc::sha512 buf;
ECDH_compute_key( (unsigned char*)&buf, sizeof(buf), EC_KEY_get0_public_key(other.my->_key), my->_key, ecies_key_derivation );
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 (detail::public_key_impl::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) );
}
} }

View file

@ -0,0 +1,152 @@
#include <fc/crypto/elliptic.hpp>
#include <fc/crypto/base58.hpp>
#include <fc/crypto/openssl.hpp>
#include <fc/fwd_impl.hpp>
#include <fc/exception/exception.hpp>
#include <fc/log/logger.hpp>
#include <assert.h>
#include <secp256k1.h>
#include "_elliptic_impl_priv.hpp"
namespace fc { namespace ecc {
namespace detail
{
const secp256k1_context_t* _get_context() {
static secp256k1_context_t* ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN);
return ctx;
}
void _init_lib() {
static const secp256k1_context_t* ctx = _get_context();
static int init_o = init_openssl();
}
class public_key_impl
{
public:
public_key_impl() noexcept
{
_init_lib();
}
public_key_impl( const public_key_impl& cpy ) noexcept
: _key( cpy._key )
{
_init_lib();
}
public_key_data _key;
};
}
static const public_key_data empty_pub;
static const private_key_secret empty_priv;
fc::sha512 private_key::get_shared_secret( const public_key& other )const
{
FC_ASSERT( my->_key != empty_priv );
FC_ASSERT( other.my->_key != empty_pub );
public_key_data pub(other.my->_key);
FC_ASSERT( secp256k1_ec_pubkey_tweak_mul( detail::_get_context(), (unsigned char*) pub.begin(), pub.size(), (unsigned char*) my->_key.data() ) );
return fc::sha512::hash( pub.begin() + 1, pub.size() - 1 );
}
public_key::public_key() {}
public_key::public_key( const public_key &pk ) : my( pk.my ) {}
public_key::public_key( public_key &&pk ) : my( std::move( pk.my ) ) {}
public_key::~public_key() {}
public_key& public_key::operator=( const public_key& pk )
{
my = pk.my;
return *this;
}
public_key& public_key::operator=( public_key&& pk )
{
my = pk.my;
return *this;
}
bool public_key::valid()const
{
return my->_key != empty_pub;
}
public_key public_key::add( const fc::sha256& digest )const
{
FC_ASSERT( my->_key != empty_pub );
public_key_data new_key;
memcpy( new_key.begin(), my->_key.begin(), new_key.size() );
FC_ASSERT( secp256k1_ec_pubkey_tweak_add( detail::_get_context(), (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 != empty_pub );
return to_base58( my->_key );
}
public_key_data public_key::serialize()const
{
FC_ASSERT( my->_key != empty_pub );
return my->_key;
}
public_key_point_data public_key::serialize_ecc_point()const
{
FC_ASSERT( my->_key != empty_pub );
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( detail::_get_context(), (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 );
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 = 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" );
}
unsigned int pk_len;
FC_ASSERT( secp256k1_ecdsa_recover_compact( detail::_get_context(), (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() );
}
} }

48
tests/ecc-interop.sh Executable file
View file

@ -0,0 +1,48 @@
#!/bin/sh
#TIME=time
cd "`dirname $0`"/..
echo Building ecc_test with openssl...
(
cmake -D ECC_IMPL=openssl .
make ecc_test
mv ecc_test ecc_test.openssl
) >/dev/null 2>&1
echo Building ecc_test with secp256k1...
(
cmake -D ECC_IMPL=secp256k1 .
make ecc_test
mv ecc_test ecc_test.secp256k1
) >/dev/null 2>&1
echo Building ecc_test with mixed...
(
cmake -D ECC_IMPL=mixed .
make ecc_test
mv ecc_test ecc_test.mixed
) >/dev/null 2>&1
run () {
echo "Running ecc_test.$1 test ecc.interop.$2 ..."
$TIME "./ecc_test.$1" test "ecc.interop.$2"
}
run openssl openssl
run openssl openssl
run secp256k1 secp256k1
run secp256k1 secp256k1
run mixed mixed
run mixed mixed
run openssl secp256k1
run openssl mixed
run secp256k1 openssl
run secp256k1 mixed
run mixed openssl
run mixed secp256k1
echo Done.
rm -f ecc_test.openssl ecc_test.secp256k1 ecc_test.mixed ecc.interop.openssl ecc.interop.secp256k1 ecc.interop.mixed

View file

@ -1,9 +1,75 @@
#include <fc/crypto/elliptic.hpp>
#include <fc/exception/exception.hpp>
#include <iostream>
#include <fstream>
static std::fstream interop_data;
static bool write_mode = false;
static void interop_do(const char * const data, size_t len) {
static char buffer[256];
if (!interop_data.is_open()) { return; }
FC_ASSERT(len < sizeof(buffer));
if (write_mode) {
interop_data.write(data, len);
return;
}
interop_data.read(buffer, len);
FC_ASSERT(!interop_data.eof());
FC_ASSERT(!memcmp(data, buffer, len));
}
static void interop_do(const fc::ecc::public_key_data &data) {
interop_do(data.begin(), data.size());
}
static void interop_do(const fc::ecc::private_key_secret &data) {
interop_do(data.data(), 32);
}
static void interop_do(const fc::ecc::public_key_point_data &data) {
interop_do(data.begin(), data.size());
}
static void interop_do(const std::string &data) {
interop_do(data.c_str(), data.length());
}
static void interop_do(const fc::sha512 &data) {
interop_do(data.data(), 64);
}
static void interop_do(fc::ecc::compact_signature &data) {
if (write_mode) {
interop_data.write((char*) data.begin(), data.size());
return;
}
interop_data.read((char*) data.begin(), data.size());
}
static void interop_file(const char * const name) {
interop_data.open(name, std::fstream::in | std::fstream::binary);
if (!interop_data.fail()) { return; }
write_mode = true;
interop_data.open(name, std::fstream::out | std::fstream::binary);
if (!interop_data.fail()) { return; }
std::cerr << "Can't read nor write " << name << "\n";
}
int main( int argc, char** argv )
{
if (argc > 2) {
interop_file(argv[2]);
}
fc::ecc::private_key nullkey;
for( uint32_t i = 0; i < 3000; ++ i )
{
try {
@ -12,16 +78,34 @@ int main( int argc, char** argv )
std::string pass(argv[1]);
fc::sha256 h = fc::sha256::hash( pass.c_str(), pass.size() );
fc::ecc::private_key priv = fc::ecc::private_key::generate_from_seed(h);
FC_ASSERT( nullkey != priv );
interop_do(priv.get_secret());
fc::ecc::public_key pub = priv.get_public_key();
interop_do(pub.serialize());
interop_do(pub.serialize_ecc_point());
pass += "1";
fc::sha256 h2 = fc::sha256::hash( pass.c_str(), pass.size() );
fc::ecc::public_key pub1 = pub.mult( h2 );
fc::ecc::public_key pub1 = pub.add( h2 );
interop_do(pub1.serialize());
interop_do(pub1.serialize_ecc_point());
fc::ecc::private_key priv1 = fc::ecc::private_key::generate_from_seed(h, h2);
interop_do(priv1.get_secret());
std::string b58 = pub1.to_base58();
interop_do(b58);
fc::ecc::public_key pub2 = fc::ecc::public_key::from_base58(b58);
FC_ASSERT( pub1 == pub2 );
fc::sha512 shared = priv1.get_shared_secret( pub );
interop_do(shared);
auto sig = priv.sign_compact( h );
interop_do(sig);
auto recover = fc::ecc::public_key( sig, h );
FC_ASSERT( recover == priv.get_public_key() );
interop_do(recover.serialize());
interop_do(recover.serialize_ecc_point());
FC_ASSERT( recover == pub );
}
catch ( const fc::exception& e )
{
@ -29,6 +113,9 @@ int main( int argc, char** argv )
}
}
if (interop_data.is_open()) {
interop_data.close();
}
return 0;
}

BIN
tests/ecc_test.interop.data Normal file

Binary file not shown.