Skip to content

Commit 088072c

Browse files
committed
Implement BIP-0062 ECDSA Signaure Check and Test
1 parent fb80505 commit 088072c

6 files changed

Lines changed: 156 additions & 38 deletions

File tree

include/fc/crypto/elliptic.hpp

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,13 @@ namespace fc {
2828
typedef fc::sha256 blinded_hash;
2929
typedef fc::sha256 blind_signature;
3030

31+
enum canonical_signature_type
32+
{
33+
non_canonical,
34+
bip_0062,
35+
fc_canonical
36+
};
37+
3138
/**
3239
* @class public_key
3340
* @brief contains only the public point of an elliptic curve key.
@@ -47,7 +54,7 @@ namespace fc {
4754

4855
public_key( const public_key_data& v );
4956
public_key( const public_key_point_data& v );
50-
public_key( const compact_signature& c, const fc::sha256& digest, bool check_canonical = true );
57+
public_key( const compact_signature& c, const fc::sha256& digest, canonical_signature_type canon_type = fc_canonical );
5158

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

@@ -78,10 +85,12 @@ namespace fc {
7885

7986
unsigned int fingerprint() const;
8087

88+
static bool is_canonical( const compact_signature& c, canonical_signature_type canon_type );
89+
8190
private:
8291
friend class private_key;
8392
static public_key from_key_data( const public_key_data& v );
84-
static bool is_canonical( const compact_signature& c );
93+
8594
fc::fwd<detail::public_key_impl,33> my;
8695
};
8796

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

125134
// signature sign( const fc::sha256& digest )const;
126-
compact_signature sign_compact( const fc::sha256& digest, bool require_canonical = true )const;
135+
compact_signature sign_compact( const fc::sha256& digest, canonical_signature_type canon_type = fc_canonical )const;
127136
// bool verify( const fc::sha256& digest, const signature& sig );
128137

129138
public_key get_public_key()const;

src/crypto/elliptic_common.cpp

Lines changed: 43 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -168,13 +168,49 @@ namespace fc { namespace ecc {
168168
return (fp[0] << 24) | (fp[1] << 16) | (fp[2] << 8) | fp[3];
169169
}
170170

171-
bool public_key::is_canonical( const compact_signature& c ) {
172-
return !(c.data[1] & 0x80)
173-
&& !(c.data[1] == 0 && !(c.data[2] & 0x80))
174-
&& !(c.data[33] & 0x80)
175-
&& !(c.data[33] == 0 && !(c.data[34] & 0x80));
171+
bool is_fc_canonical( const compact_signature& c )
172+
{
173+
return !(c.data[1] & 0x80)
174+
&& !(c.data[1] == 0 && !(c.data[2] & 0x80))
175+
&& !(c.data[33] & 0x80)
176+
&& !(c.data[33] == 0 && !(c.data[34] & 0x80));
177+
}
178+
179+
bool is_bip_0062_canonical( const compact_signature& c )
180+
{
181+
// N/2 is 0x7FFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF 5D576E73 57A4501D DFE92F46 681B20A0
182+
// According to BIP-0062, s must be <= N/2
183+
const static uint8_t n_2[32] = {
184+
0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
185+
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
186+
0x5D, 0x57, 0x6E, 0x73, 0x57, 0xA4, 0x50, 0x1D,
187+
0xDF, 0xE9, 0x2F, 0x46, 0x68, 0x1B, 0x20, 0xA0
188+
};
189+
190+
for( size_t i = 0; i < 31; ++i )
191+
{
192+
if( c.data[33 + i ] != n_2[i] )
193+
return c.data[33 + i ] < n_2[i];
194+
}
195+
196+
return c.data[64] <= n_2[31];
176197
}
177198

199+
200+
bool public_key::is_canonical( const compact_signature& c, canonical_signature_type canon_type )
201+
{
202+
switch( canon_type )
203+
{
204+
case bip_0062:
205+
return is_bip_0062_canonical( c );
206+
case fc_canonical:
207+
return is_fc_canonical( c );
208+
case non_canonical:
209+
return true;
210+
}
211+
}
212+
213+
178214
private_key private_key::generate_from_seed( const fc::sha256& seed, const fc::sha256& offset )
179215
{
180216
ssl_bignum z;
@@ -269,7 +305,7 @@ namespace fc { namespace ecc {
269305
memcpy( dest, key.begin(), key.size() );
270306
return result;
271307
}
272-
308+
273309
extended_public_key extended_public_key::deserialize( const extended_key_data& data )
274310
{
275311
return from_base58( _to_base58( data ) );
@@ -340,7 +376,7 @@ namespace fc { namespace ecc {
340376
memcpy( dest, key.data(), key.data_size() );
341377
return result;
342378
}
343-
379+
344380
extended_private_key extended_private_key::deserialize( const extended_key_data& data )
345381
{
346382
return from_base58( _to_base58( data ) );

src/crypto/elliptic_impl_priv.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ namespace fc { namespace ecc {
8585
return secp256k1_nonce_function_default( nonce32, msg32, key32, *extra, nullptr );
8686
}
8787

88-
compact_signature private_key::sign_compact( const fc::sha256& digest, bool require_canonical )const
88+
compact_signature private_key::sign_compact( const fc::sha256& digest, canonical_signature_type canon_type )const
8989
{
9090
FC_ASSERT( my->_key != empty_priv );
9191
compact_signature result;
@@ -94,7 +94,7 @@ namespace fc { namespace ecc {
9494
do
9595
{
9696
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 ));
97-
} while( require_canonical && !public_key::is_canonical( result ) );
97+
} while( !public_key::is_canonical( result, canon_type ) );
9898
result.begin()[0] = 27 + 4 + recid;
9999
return result;
100100
}

src/crypto/elliptic_secp256k1.cpp

Lines changed: 23 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -152,16 +152,13 @@ namespace fc { namespace ecc {
152152
my->_key = dat;
153153
}
154154

155-
public_key::public_key( const compact_signature& c, const fc::sha256& digest, bool check_canonical )
155+
public_key::public_key( const compact_signature& c, const fc::sha256& digest, canonical_signature_type canon_type )
156156
{
157157
int nV = c.data[0];
158158
if (nV<27 || nV>=35)
159159
FC_THROW_EXCEPTION( exception, "unable to reconstruct public key from signature" );
160160

161-
if( check_canonical )
162-
{
163-
FC_ASSERT( is_canonical( c ), "signature is not canonical" );
164-
}
161+
FC_ASSERT( is_canonical( c, canon_type ), "signature is not canonical" );
165162

166163
unsigned int pk_len;
167164
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 ) );
@@ -487,24 +484,24 @@ namespace fc { namespace ecc {
487484
return secp256k1_rangeproof_verify( detail::_get_context(), &min_val, &max_val, (const unsigned char*)&commit, (const unsigned char*)proof.data(), proof.size() );
488485
}
489486

490-
std::vector<char> range_proof_sign( uint64_t min_value,
491-
const commitment_type& commit,
492-
const blind_factor_type& commit_blind,
487+
std::vector<char> range_proof_sign( uint64_t min_value,
488+
const commitment_type& commit,
489+
const blind_factor_type& commit_blind,
493490
const blind_factor_type& nonce,
494491
int8_t base10_exp,
495492
uint8_t min_bits,
496493
uint64_t actual_value
497494
)
498495
{
499-
int proof_len = 5134;
496+
int proof_len = 5134;
500497
std::vector<char> proof(proof_len);
501498

502-
FC_ASSERT( secp256k1_rangeproof_sign( detail::_get_context(),
503-
(unsigned char*)proof.data(),
504-
&proof_len, min_value,
505-
(const unsigned char*)&commit,
506-
(const unsigned char*)&commit_blind,
507-
(const unsigned char*)&nonce,
499+
FC_ASSERT( secp256k1_rangeproof_sign( detail::_get_context(),
500+
(unsigned char*)proof.data(),
501+
&proof_len, min_value,
502+
(const unsigned char*)&commit,
503+
(const unsigned char*)&commit_blind,
504+
(const unsigned char*)&nonce,
508505
base10_exp, min_bits, actual_value ) );
509506
proof.resize(proof_len);
510507
return proof;
@@ -513,16 +510,16 @@ namespace fc { namespace ecc {
513510

514511
bool verify_range_proof_rewind( blind_factor_type& blind_out,
515512
uint64_t& value_out,
516-
string& message_out,
513+
string& message_out,
517514
const blind_factor_type& nonce,
518-
uint64_t& min_val,
519-
uint64_t& max_val,
520-
commitment_type commit,
515+
uint64_t& min_val,
516+
uint64_t& max_val,
517+
commitment_type commit,
521518
const std::vector<char>& proof )
522519
{
523520
char msg[4096];
524521
int mlen = 0;
525-
FC_ASSERT( secp256k1_rangeproof_rewind( detail::_get_context(),
522+
FC_ASSERT( secp256k1_rangeproof_rewind( detail::_get_context(),
526523
(unsigned char*)&blind_out,
527524
&value_out,
528525
(unsigned char*)msg,
@@ -541,12 +538,12 @@ namespace fc { namespace ecc {
541538
range_proof_info range_get_info( const std::vector<char>& proof )
542539
{
543540
range_proof_info result;
544-
FC_ASSERT( secp256k1_rangeproof_info( detail::_get_context(),
545-
(int*)&result.exp,
546-
(int*)&result.mantissa,
547-
(uint64_t*)&result.min_value,
548-
(uint64_t*)&result.max_value,
549-
(const unsigned char*)proof.data(),
541+
FC_ASSERT( secp256k1_rangeproof_info( detail::_get_context(),
542+
(int*)&result.exp,
543+
(int*)&result.mantissa,
544+
(uint64_t*)&result.min_value,
545+
(uint64_t*)&result.max_value,
546+
(const unsigned char*)proof.data(),
550547
(int)proof.size() ) );
551548

552549
return result;

tests/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ target_link_libraries( ecc_test fc )
3535
add_executable( log_test crypto/log_test.cpp )
3636
target_link_libraries( log_test fc )
3737

38+
add_executable( ecdsa_canon_test crypto/ecdsa_canon_test.cpp )
39+
target_link_libraries( ecdsa_canon_test fc )
40+
3841
#add_executable( test_aes aes_test.cpp )
3942
#target_link_libraries( test_aes fc ${rt_library} ${pthread_library} )
4043
#add_executable( test_sleep sleep.cpp )
@@ -55,6 +58,7 @@ add_executable( all_tests all_tests.cpp
5558
crypto/dh_test.cpp
5659
crypto/rand_test.cpp
5760
crypto/sha_tests.cpp
61+
crypto/ecdsa_canon_test.cpp
5862
network/ntp_test.cpp
5963
network/http/websocket_test.cpp
6064
thread/task_cancel.cpp

tests/crypto/ecdsa_canon_test.cpp

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
#include <fc/crypto/elliptic.hpp>
2+
#include <fc/exception/exception.hpp>
3+
4+
#include <iostream>
5+
6+
uint8_t fc_canon[65] = {
7+
/*rec id */ 0x20,
8+
/* 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,
9+
/* 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
10+
};
11+
12+
uint8_t bip_0062_canon[65] = {
13+
/*rec id */ 0x20,
14+
/* 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,
15+
/* 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
16+
};
17+
18+
uint8_t bip_0062_canon2[65] = {
19+
/*rec id */ 0x20,
20+
/* 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,
21+
/* 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
22+
};
23+
24+
// -s (mod n)
25+
uint8_t non_canon[65] = {
26+
/*rec id */ 0x20,
27+
/* 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,
28+
/* 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
29+
};
30+
31+
int main( int argc, char** argv )
32+
{
33+
try
34+
{
35+
fc::ecc::compact_signature fc_canon_sig;
36+
memcpy( fc_canon_sig.data, fc_canon, sizeof( unsigned char ) * 65 );
37+
38+
fc::ecc::compact_signature bip_0062_canon_sig;
39+
memcpy( bip_0062_canon_sig.data, bip_0062_canon, sizeof( unsigned char ) * 65 );
40+
41+
fc::ecc::compact_signature bip_0062_canon2_sig;
42+
memcpy( bip_0062_canon2_sig.data, bip_0062_canon2, sizeof( unsigned char ) * 65 );
43+
44+
fc::ecc::compact_signature non_canon_sig;
45+
memcpy( non_canon_sig.data, non_canon, sizeof( unsigned char ) * 65 );
46+
47+
ilog( "Testing non-canonical validation" );
48+
FC_ASSERT( fc::ecc::public_key::is_canonical( fc_canon_sig, fc::ecc::canonical_signature_type::non_canonical ) );
49+
FC_ASSERT( fc::ecc::public_key::is_canonical( bip_0062_canon_sig, fc::ecc::canonical_signature_type::non_canonical ) );
50+
FC_ASSERT( fc::ecc::public_key::is_canonical( bip_0062_canon2_sig, fc::ecc::canonical_signature_type::non_canonical ) );
51+
FC_ASSERT( fc::ecc::public_key::is_canonical( non_canon_sig, fc::ecc::canonical_signature_type::non_canonical ) );
52+
53+
ilog( "Testing bip_0062 canonical validation" );
54+
FC_ASSERT( fc::ecc::public_key::is_canonical( fc_canon_sig, fc::ecc::canonical_signature_type::bip_0062 ) );
55+
FC_ASSERT( fc::ecc::public_key::is_canonical( bip_0062_canon_sig, fc::ecc::canonical_signature_type::bip_0062 ) );
56+
FC_ASSERT( fc::ecc::public_key::is_canonical( bip_0062_canon2_sig, fc::ecc::canonical_signature_type::bip_0062 ) );
57+
FC_ASSERT( !fc::ecc::public_key::is_canonical( non_canon_sig, fc::ecc::canonical_signature_type::bip_0062 ) );
58+
59+
ilog( "Testing fc canonical validation" );
60+
FC_ASSERT( fc::ecc::public_key::is_canonical( fc_canon_sig, fc::ecc::canonical_signature_type::fc_canonical ) );
61+
FC_ASSERT( !fc::ecc::public_key::is_canonical( bip_0062_canon_sig, fc::ecc::canonical_signature_type::fc_canonical ) );
62+
FC_ASSERT( fc::ecc::public_key::is_canonical( bip_0062_canon2_sig, fc::ecc::canonical_signature_type::fc_canonical ) );
63+
FC_ASSERT( !fc::ecc::public_key::is_canonical( non_canon_sig, fc::ecc::canonical_signature_type::fc_canonical ) );
64+
}
65+
catch( fc::exception& e )
66+
{
67+
ilog( "Uncaught Exception: ${e}", ("e", e) );
68+
return 1;
69+
}
70+
71+
return 0;
72+
}

0 commit comments

Comments
 (0)