Skip to content

Commit

Permalink
Implement BIP-0062 ECDSA Signaure Check and Test
Browse files Browse the repository at this point in the history
  • Loading branch information
mvandeberg committed Dec 28, 2017
1 parent fb80505 commit 088072c
Show file tree
Hide file tree
Showing 6 changed files with 156 additions and 38 deletions.
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
50 changes: 43 additions & 7 deletions src/crypto/elliptic_common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -168,13 +168,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 )
{
// N/2 is 0x7FFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF 5D576E73 57A4501D DFE92F46 681B20A0
// According to BIP-0062, s must be <= N/2
const static uint8_t n_2[32] = {
0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x5D, 0x57, 0x6E, 0x73, 0x57, 0xA4, 0x50, 0x1D,
0xDF, 0xE9, 0x2F, 0x46, 0x68, 0x1B, 0x20, 0xA0
};

for( size_t i = 0; i < 31; ++i )
{
if( c.data[33 + i ] != n_2[i] )
return c.data[33 + i ] < n_2[i];
}

return c.data[64] <= n_2[31];
}


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 +305,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 +376,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 ) );
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;
}

0 comments on commit 088072c

Please sign in to comment.