diff --git a/include/fc/crypto/elliptic.hpp b/include/fc/crypto/elliptic.hpp index d31d1c1..778f249 100644 --- a/include/fc/crypto/elliptic.hpp +++ b/include/fc/crypto/elliptic.hpp @@ -197,6 +197,7 @@ namespace fc { public_key blind_public_key( const extended_public_key& bob, int i ) const; blinded_hash blind_hash( const fc::sha256& hash, int i ) const; blind_signature blind_sign( const blinded_hash& hash, int i ) const; + // WARNING! This may produce non-canonical signatures! compact_signature unblind_signature( const extended_public_key& bob, const blind_signature& sig, int i ) const; private: diff --git a/src/crypto/elliptic_secp256k1.cpp b/src/crypto/elliptic_secp256k1.cpp index ee0b785..7a6c232 100644 --- a/src/crypto/elliptic_secp256k1.cpp +++ b/src/crypto/elliptic_secp256k1.cpp @@ -178,35 +178,45 @@ namespace fc { namespace ecc { return result; } - static void to_bignum( const private_key_secret& in, ssl_bignum& out ) + static void to_bignum( const unsigned char* in, ssl_bignum& out, unsigned int len ) { - if ( in.data()[0] & 0x80 ) + if ( *in & 0x80 ) { - unsigned char buffer[33]; + unsigned char buffer[len + 1]; *buffer = 0; - memcpy( buffer + 1, in.data(), 32 ); + memcpy( buffer + 1, in, len ); BN_bin2bn( buffer, sizeof(buffer), out ); } else { - BN_bin2bn( (unsigned char*) in.data(), in.data_size(), out ); + BN_bin2bn( in, len, out ); + } + } + + static void to_bignum( const private_key_secret& in, ssl_bignum& out ) + { + to_bignum( (unsigned char*) in.data(), out, in.data_size() ); + } + + static void from_bignum( const ssl_bignum& in, unsigned char* out, unsigned int len ) + { + unsigned int l = BN_num_bytes( in ); + if ( l > len ) + { + unsigned char buffer[l]; + BN_bn2bin( in, buffer ); + memcpy( out, buffer + l - len, len ); + } + else + { + memset( out, 0, len - l ); + BN_bn2bin( in, out + len - l ); } } static void from_bignum( const ssl_bignum& in, private_key_secret& out ) { - unsigned int len = BN_num_bytes( in ); - if ( len > out.data_size() ) - { - unsigned char buffer[len]; - BN_bn2bin( in, buffer ); - memcpy( (unsigned char*) out.data(), buffer + len - out.data_size(), out.data_size() ); - } - else - { - memset( out.data(), 0, out.data_size() - len ); - BN_bn2bin( in, (unsigned char*) out.data() + out.data_size() - len ); - } + from_bignum( in, (unsigned char*) out.data(), out.data_size() ); } static void invert( const private_key_secret& in, private_key_secret& out ) @@ -256,6 +266,20 @@ namespace fc { namespace ecc { return public_key( P ); } + static void canonicalize( unsigned char *int256 ) + { + if (!(*int256 & 0x80)) + { + return; // nothing to do + } + ssl_bignum bn_k; + to_bignum( int256, bn_k, 32 ); + ssl_bignum bn_n; + to_bignum( detail::get_curve_order(), bn_n ); + FC_ASSERT( BN_sub( bn_k, bn_n, bn_k ) ); + from_bignum( bn_k, int256, 32 ); + } + static public_key compute_t( const private_key_secret& a, const private_key_secret& b, const private_key_secret& c, const private_key_secret& d, const public_key_data& p, const public_key_data& q ) @@ -369,8 +393,11 @@ namespace fc { namespace ecc { public_key_data k = compute_k( a, c, p ); compact_signature result; - memcpy( result.begin(), k.begin() + 1, 32 ); - memcpy( result.begin() + 32, c.data(), 32 ); + *result.begin() = 27 + 4 + (*k.begin() & 1); + memcpy( result.begin() + 1, k.begin() + 1, 32 ); + canonicalize( result.begin() + 1 ); + memcpy( result.begin() + 33, c.data(), 32 ); + canonicalize( result.begin() + 33 ); return result; } diff --git a/tests/blinding_test.cpp b/tests/blinding_test.cpp index 2b64167..65dc77d 100644 --- a/tests/blinding_test.cpp +++ b/tests/blinding_test.cpp @@ -4,6 +4,7 @@ #include #include #include +#include // See https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#Test_Vectors @@ -103,3 +104,29 @@ BOOST_AUTO_TEST_CASE(test_extended_keys_2) BOOST_CHECK_EQUAL( m_0_m1_1_m2_2.get_extended_public_key().str(), TEST2_M_0_m1_1_m2_2_PUB ); BOOST_CHECK_EQUAL( m_0_m1_1_m2.get_extended_public_key().derive_child(2).str(), TEST2_M_0_m1_1_m2_2_PUB ); } + +BOOST_AUTO_TEST_CASE(test_blinding) +{ + char buffer[7] = "test_"; + fc::ecc::extended_private_key alice = fc::ecc::extended_private_key::generate_master( "master" ); + fc::ecc::extended_private_key bob = fc::ecc::extended_private_key::generate_master( "puppet" ); + + for ( int i = 0; i < 10; i++ ) + { + alice = alice.derive_child( i ); + bob = bob.derive_child( i | 0x80000000 ); + buffer[6] = '0' + i; + fc::ecc::extended_public_key bob_pub = bob.get_extended_public_key(); + fc::sha256 hash = fc::sha256::hash( buffer, sizeof(buffer) ); + fc::ecc::public_key t = alice.blind_public_key( bob_pub, i ); + fc::ecc::blinded_hash blinded = alice.blind_hash( hash, i ); + fc::ecc::blind_signature blind_sig = bob.blind_sign( blinded, i ); + fc::ecc::compact_signature sig = alice.unblind_signature( bob_pub, blind_sig, i ); + try { + fc::ecc::public_key validate( sig, hash ); +// BOOST_CHECK_EQUAL( validate.serialize(), t.serialize() ); + } catch (const fc::exception& e) { + printf( "Test %d: %s\n", i, e.to_string().c_str() ); + } + } +}