Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BIP-0062 #34

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions include/fc/crypto/elliptic.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ namespace fc {
typedef fc::sha256 blinded_hash;
typedef fc::sha256 blind_signature;

enum canonical_signature_type
{
non_canonical,
bip_0062,
fc_canonical
};

/**
* @class public_key
* @brief contains only the public point of an elliptic curve key.
Expand All @@ -47,7 +54,7 @@ namespace fc {

public_key( const public_key_data& v );
public_key( const public_key_point_data& v );
public_key( const compact_signature& c, const fc::sha256& digest, bool check_canonical = true );
public_key( const compact_signature& c, const fc::sha256& digest, canonical_signature_type canon_type = fc_canonical );

public_key child( const fc::sha256& offset )const;

Expand Down Expand Up @@ -78,10 +85,12 @@ namespace fc {

unsigned int fingerprint() const;

static bool is_canonical( const compact_signature& c, canonical_signature_type canon_type );

private:
friend class private_key;
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;
};

Expand Down Expand Up @@ -123,7 +132,7 @@ namespace fc {
fc::sha512 get_shared_secret( const public_key& pub )const;

// signature sign( const fc::sha256& digest )const;
compact_signature sign_compact( const fc::sha256& digest, bool require_canonical = true )const;
compact_signature sign_compact( const fc::sha256& digest, canonical_signature_type canon_type = fc_canonical )const;
// bool verify( const fc::sha256& digest, const signature& sig );

public_key get_public_key()const;
Expand Down
55 changes: 48 additions & 7 deletions src/crypto/elliptic_common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
#include <fc/crypto/openssl.hpp>
#include <fc/crypto/ripemd160.hpp>

#include <boost/endian/conversion.hpp>
#include <boost/multiprecision/cpp_int.hpp>

#ifdef _WIN32
# include <malloc.h>
#else
Expand All @@ -18,6 +21,8 @@

namespace fc { namespace ecc {

using namespace boost::multiprecision::literals;

namespace detail {
typedef fc::array<char,37> chr37;

Expand Down Expand Up @@ -168,13 +173,49 @@ namespace fc { namespace ecc {
return (fp[0] << 24) | (fp[1] << 16) | (fp[2] << 8) | fp[3];
}

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));
bool is_fc_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));
}

bool is_bip_0062_canonical( const compact_signature& c )
{
constexpr boost::multiprecision::uint256_t n_2 =
0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0_cppui256;

boost::multiprecision::uint256_t sig = 0;

// boost endian conversions are only supported for native c++ types,
// so we need to convert in 64 bit words
for( size_t i = 0; i < 4; i++ )
{
sig <<= 64;
sig += boost::endian::big_to_native( *( uint64_t* )( c.data + 33 + ( i * 8 ) ) );
}

// BIP-0062 states that sig must be in [1,n/2], however because a sig of value 0 is an invalid
// signature under all circumstances, the lower bound does not need checking
return sig <= n_2;
}


bool public_key::is_canonical( const compact_signature& c, canonical_signature_type canon_type )
{
switch( canon_type )
{
case bip_0062:
return is_bip_0062_canonical( c );
case fc_canonical:
return is_fc_canonical( c );
case non_canonical:
return true;
}
}


private_key private_key::generate_from_seed( const fc::sha256& seed, const fc::sha256& offset )
{
ssl_bignum z;
Expand Down Expand Up @@ -269,7 +310,7 @@ namespace fc { namespace ecc {
memcpy( dest, key.begin(), key.size() );
return result;
}

extended_public_key extended_public_key::deserialize( const extended_key_data& data )
{
return from_base58( _to_base58( data ) );
Expand Down Expand Up @@ -340,7 +381,7 @@ namespace fc { namespace ecc {
memcpy( dest, key.data(), key.data_size() );
return result;
}

extended_private_key extended_private_key::deserialize( const extended_key_data& data )
{
return from_base58( _to_base58( data ) );
Expand Down
4 changes: 2 additions & 2 deletions src/crypto/elliptic_impl_priv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ namespace fc { namespace ecc {
return secp256k1_nonce_function_default( nonce32, msg32, key32, *extra, nullptr );
}

compact_signature private_key::sign_compact( const fc::sha256& digest, bool require_canonical )const
compact_signature private_key::sign_compact( const fc::sha256& digest, canonical_signature_type canon_type )const
{
FC_ASSERT( my->_key != empty_priv );
compact_signature result;
Expand All @@ -94,7 +94,7 @@ namespace fc { namespace ecc {
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( require_canonical && !public_key::is_canonical( result ) );
} while( !public_key::is_canonical( result, canon_type ) );
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@goldibex What about loop it self, is it okay to generate signature in the loop? There is a function secp256k1_ecdsa_signature_normalize which should do it.

you can find more here: https://github.com/bitcoin-core/secp256k1/blob/master/include/secp256k1.h#L461

result.begin()[0] = 27 + 4 + recid;
return result;
}
Expand Down
49 changes: 23 additions & 26 deletions src/crypto/elliptic_secp256k1.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -152,16 +152,13 @@ namespace fc { namespace ecc {
my->_key = dat;
}

public_key::public_key( const compact_signature& c, const fc::sha256& digest, bool check_canonical )
public_key::public_key( const compact_signature& c, const fc::sha256& digest, canonical_signature_type canon_type )
{
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" );
}
FC_ASSERT( is_canonical( c, canon_type ), "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 ) );
Expand Down Expand Up @@ -487,24 +484,24 @@ namespace fc { namespace ecc {
return secp256k1_rangeproof_verify( detail::_get_context(), &min_val, &max_val, (const unsigned char*)&commit, (const unsigned char*)proof.data(), proof.size() );
}

std::vector<char> range_proof_sign( uint64_t min_value,
const commitment_type& commit,
const blind_factor_type& commit_blind,
std::vector<char> range_proof_sign( uint64_t min_value,
const commitment_type& commit,
const blind_factor_type& commit_blind,
const blind_factor_type& nonce,
int8_t base10_exp,
uint8_t min_bits,
uint64_t actual_value
)
{
int proof_len = 5134;
int proof_len = 5134;
std::vector<char> proof(proof_len);

FC_ASSERT( secp256k1_rangeproof_sign( detail::_get_context(),
(unsigned char*)proof.data(),
&proof_len, min_value,
(const unsigned char*)&commit,
(const unsigned char*)&commit_blind,
(const unsigned char*)&nonce,
FC_ASSERT( secp256k1_rangeproof_sign( detail::_get_context(),
(unsigned char*)proof.data(),
&proof_len, min_value,
(const unsigned char*)&commit,
(const unsigned char*)&commit_blind,
(const unsigned char*)&nonce,
base10_exp, min_bits, actual_value ) );
proof.resize(proof_len);
return proof;
Expand All @@ -513,16 +510,16 @@ namespace fc { namespace ecc {

bool verify_range_proof_rewind( blind_factor_type& blind_out,
uint64_t& value_out,
string& message_out,
string& message_out,
const blind_factor_type& nonce,
uint64_t& min_val,
uint64_t& max_val,
commitment_type commit,
uint64_t& min_val,
uint64_t& max_val,
commitment_type commit,
const std::vector<char>& proof )
{
char msg[4096];
int mlen = 0;
FC_ASSERT( secp256k1_rangeproof_rewind( detail::_get_context(),
FC_ASSERT( secp256k1_rangeproof_rewind( detail::_get_context(),
(unsigned char*)&blind_out,
&value_out,
(unsigned char*)msg,
Expand All @@ -541,12 +538,12 @@ namespace fc { namespace ecc {
range_proof_info range_get_info( const std::vector<char>& proof )
{
range_proof_info result;
FC_ASSERT( secp256k1_rangeproof_info( detail::_get_context(),
(int*)&result.exp,
(int*)&result.mantissa,
(uint64_t*)&result.min_value,
(uint64_t*)&result.max_value,
(const unsigned char*)proof.data(),
FC_ASSERT( secp256k1_rangeproof_info( detail::_get_context(),
(int*)&result.exp,
(int*)&result.mantissa,
(uint64_t*)&result.min_value,
(uint64_t*)&result.max_value,
(const unsigned char*)proof.data(),
(int)proof.size() ) );

return result;
Expand Down
4 changes: 4 additions & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ target_link_libraries( ecc_test fc )
add_executable( log_test crypto/log_test.cpp )
target_link_libraries( log_test fc )

add_executable( ecdsa_canon_test crypto/ecdsa_canon_test.cpp )
target_link_libraries( ecdsa_canon_test fc )

#add_executable( test_aes aes_test.cpp )
#target_link_libraries( test_aes fc ${rt_library} ${pthread_library} )
#add_executable( test_sleep sleep.cpp )
Expand All @@ -55,6 +58,7 @@ add_executable( all_tests all_tests.cpp
crypto/dh_test.cpp
crypto/rand_test.cpp
crypto/sha_tests.cpp
crypto/ecdsa_canon_test.cpp
network/ntp_test.cpp
network/http/websocket_test.cpp
thread/task_cancel.cpp
Expand Down
72 changes: 72 additions & 0 deletions tests/crypto/ecdsa_canon_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#include <fc/crypto/elliptic.hpp>
#include <fc/exception/exception.hpp>

#include <iostream>

uint8_t fc_canon[65] = {
/*rec id */ 0x20,
/* r */ 0x12, 0x65, 0xbb, 0xa6, 0xde, 0xb1, 0xba, 0xf0, 0x79, 0x3b, 0xc5, 0x08, 0x77, 0x99, 0x27, 0x2b, 0x5d, 0x2e, 0xf6, 0xff, 0x9d, 0x72, 0x21, 0x8a, 0x68, 0x82, 0x25, 0x9d, 0x98, 0x94, 0xda, 0xd7,
/* s */ 0x66, 0x36, 0x6a, 0x28, 0xfa, 0x4d, 0xb9, 0x06, 0x66, 0x1e, 0x3b, 0xf2, 0x68, 0x2d, 0x27, 0x9b, 0xeb, 0x9d, 0x2f, 0x4c, 0xc4, 0x36, 0xee, 0xbf, 0xa3, 0x52, 0xe8, 0x4f, 0xc5, 0xd0, 0x47, 0xee
};

uint8_t bip_0062_canon[65] = {
/*rec id */ 0x20,
/* r */ 0xe3, 0x9a, 0xff, 0x4c, 0x7a, 0xea, 0x6f, 0xe8, 0x50, 0xad, 0x9f, 0x45, 0x4a, 0xdc, 0x59, 0x61, 0x15, 0xa8, 0xfb, 0x85, 0xd7, 0xb6, 0xaa, 0x2d, 0x2a, 0x31, 0xbe, 0x84, 0x05, 0x85, 0x93, 0x5f,
/* s */ 0x56, 0x6d, 0x35, 0x9d, 0x1f, 0x57, 0x6a, 0x59, 0xb5, 0x0c, 0x4e, 0x31, 0x45, 0x04, 0x24, 0x43, 0x5a, 0x37, 0x1c, 0x35, 0x02, 0x2a, 0xab, 0x20, 0x1f, 0xcf, 0x55, 0x1b, 0xee, 0x58, 0xdd, 0x10
};

uint8_t bip_0062_canon2[65] = {
/*rec id */ 0x20,
/* r */ 0x12, 0x65, 0xbb, 0xa6, 0xde, 0xb1, 0xba, 0xf0, 0x79, 0x3b, 0xc5, 0x08, 0x77, 0x99, 0x27, 0x2b, 0x5d, 0x2e, 0xf6, 0xff, 0x9d, 0x72, 0x21, 0x8a, 0x68, 0x82, 0x25, 0x9d, 0x98, 0x94, 0xda, 0xd7,
/* s */ 0x2f, 0x11, 0x18, 0xe5, 0xca, 0xce, 0xea, 0xce, 0x60, 0x2c, 0x8d, 0x97, 0xcb, 0x15, 0x87, 0xc1, 0x06, 0xa1, 0xe6, 0x94, 0x05, 0xe8, 0x62, 0x1e, 0xae, 0x50, 0x3c, 0xe4, 0x04, 0xad, 0x8f, 0x29
};

// -s (mod n)
uint8_t non_canon[65] = {
/*rec id */ 0x20,
/* r */ 0x12, 0x65, 0xbb, 0xa6, 0xde, 0xb1, 0xba, 0xf0, 0x79, 0x3b, 0xc5, 0x08, 0x77, 0x99, 0x27, 0x2b, 0x5d, 0x2e, 0xf6, 0xff, 0x9d, 0x72, 0x21, 0x8a, 0x68, 0x82, 0x25, 0x9d, 0x98, 0x94, 0xda, 0xd7,
/* s */ 0xa9, 0x92, 0xca, 0x62, 0xe0, 0xa8, 0x95, 0xa6, 0x4a, 0xf3, 0xb1, 0xce, 0xba, 0xfb, 0xdb, 0xbc, 0x60, 0x77, 0xc0, 0xb1, 0xad, 0x1d, 0xf5, 0x1b, 0xa0, 0x03, 0x09, 0x70, 0xe1, 0xdd, 0x64, 0x31
};

int main( int argc, char** argv )
{
try
{
fc::ecc::compact_signature fc_canon_sig;
memcpy( fc_canon_sig.data, fc_canon, sizeof( unsigned char ) * 65 );

fc::ecc::compact_signature bip_0062_canon_sig;
memcpy( bip_0062_canon_sig.data, bip_0062_canon, sizeof( unsigned char ) * 65 );

fc::ecc::compact_signature bip_0062_canon2_sig;
memcpy( bip_0062_canon2_sig.data, bip_0062_canon2, sizeof( unsigned char ) * 65 );

fc::ecc::compact_signature non_canon_sig;
memcpy( non_canon_sig.data, non_canon, sizeof( unsigned char ) * 65 );

ilog( "Testing non-canonical validation" );
FC_ASSERT( fc::ecc::public_key::is_canonical( fc_canon_sig, fc::ecc::canonical_signature_type::non_canonical ) );
FC_ASSERT( fc::ecc::public_key::is_canonical( bip_0062_canon_sig, fc::ecc::canonical_signature_type::non_canonical ) );
FC_ASSERT( fc::ecc::public_key::is_canonical( bip_0062_canon2_sig, fc::ecc::canonical_signature_type::non_canonical ) );
FC_ASSERT( fc::ecc::public_key::is_canonical( non_canon_sig, fc::ecc::canonical_signature_type::non_canonical ) );

ilog( "Testing bip_0062 canonical validation" );
FC_ASSERT( fc::ecc::public_key::is_canonical( fc_canon_sig, fc::ecc::canonical_signature_type::bip_0062 ) );
FC_ASSERT( fc::ecc::public_key::is_canonical( bip_0062_canon_sig, fc::ecc::canonical_signature_type::bip_0062 ) );
FC_ASSERT( fc::ecc::public_key::is_canonical( bip_0062_canon2_sig, fc::ecc::canonical_signature_type::bip_0062 ) );
FC_ASSERT( !fc::ecc::public_key::is_canonical( non_canon_sig, fc::ecc::canonical_signature_type::bip_0062 ) );

ilog( "Testing fc canonical validation" );
FC_ASSERT( fc::ecc::public_key::is_canonical( fc_canon_sig, fc::ecc::canonical_signature_type::fc_canonical ) );
FC_ASSERT( !fc::ecc::public_key::is_canonical( bip_0062_canon_sig, fc::ecc::canonical_signature_type::fc_canonical ) );
FC_ASSERT( fc::ecc::public_key::is_canonical( bip_0062_canon2_sig, fc::ecc::canonical_signature_type::fc_canonical ) );
FC_ASSERT( !fc::ecc::public_key::is_canonical( non_canon_sig, fc::ecc::canonical_signature_type::fc_canonical ) );
}
catch( fc::exception& e )
{
ilog( "Uncaught Exception: ${e}", ("e", e) );
return 1;
}

return 0;
}