diff --git a/include/fc/crypto/elliptic.hpp b/include/fc/crypto/elliptic.hpp index 600d7b3..d31d1c1 100644 --- a/include/fc/crypto/elliptic.hpp +++ b/include/fc/crypto/elliptic.hpp @@ -25,6 +25,8 @@ namespace fc { typedef fc::array compact_signature; typedef std::vector range_proof_type; typedef fc::array extended_key_data; + typedef fc::sha256 blinded_hash; + typedef fc::sha256 blind_signature; /** * @class public_key @@ -162,6 +164,8 @@ namespace fc { fc::string to_base58() const { return str(); } static extended_public_key from_base58( const fc::string& base58 ); + public_key generate_p( int i ) const; + public_key generate_q( int i ) const; private: sha256 c; int child_num, parent_fp; @@ -188,10 +192,22 @@ namespace fc { static extended_private_key generate_master( const fc::string& seed ); static extended_private_key generate_master( const char* seed, uint32_t seed_len ); + // Oleg Andreev's blind signature scheme, + // see http://blog.oleganza.com/post/77474860538/blind-signatures + 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; + compact_signature unblind_signature( const extended_public_key& bob, const blind_signature& sig, int i ) const; + private: extended_private_key private_derive_rest( const fc::sha512& hash, int num ) const; - + private_key generate_a( int i ) const; + private_key generate_b( int i ) const; + private_key generate_c( int i ) const; + private_key generate_d( int i ) const; + private_key_secret compute_p( int i ) const; + private_key_secret compute_q( int i, const private_key_secret& p ) const; sha256 c; int child_num, parent_fp; uint8_t depth; @@ -211,9 +227,9 @@ namespace fc { bool verify_sum( const std::vector& commits, const std::vector& neg_commits, int64_t excess ); bool verify_range( uint64_t& min_val, uint64_t& max_val, const commitment_type& commit, const range_proof_type& proof ); - range_proof_type range_proof_sign( uint64_t min_value, - const commitment_type& commit, - const blind_factor_type& commit_blind, + range_proof_type 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, @@ -222,15 +238,15 @@ namespace fc { 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 range_proof_type& proof ); range_proof_info range_get_info( const range_proof_type& proof ); - - + + } // namespace ecc void to_variant( const ecc::private_key& var, variant& vo ); diff --git a/include/fc/crypto/openssl.hpp b/include/fc/crypto/openssl.hpp index 5811390..af883d6 100644 --- a/include/fc/crypto/openssl.hpp +++ b/include/fc/crypto/openssl.hpp @@ -22,11 +22,10 @@ namespace fc { ssl_wrapper(ssl_type* obj):obj(obj) {} - operator ssl_type*() - { - return obj; - } + operator ssl_type*() { return obj; } + operator const ssl_type*() const { return obj; } ssl_type* operator->() { return obj; } + const ssl_type* operator->() const { return obj; } ssl_type* obj; }; diff --git a/src/crypto/elliptic_common.cpp b/src/crypto/elliptic_common.cpp index 6de620d..f673035 100644 --- a/src/crypto/elliptic_common.cpp +++ b/src/crypto/elliptic_common.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include /* stuff common to all ecc implementations */ @@ -64,6 +65,30 @@ namespace fc { namespace ecc { { return _derive_message( 0, key.data(), i ); } + + const ec_group& get_curve() + { + static const ec_group secp256k1( EC_GROUP_new_by_curve_name( NID_secp256k1 ) ); + return secp256k1; + } + + static private_key_secret _get_curve_order() + { + const ec_group& group = get_curve(); + bn_ctx ctx(BN_CTX_new()); + ssl_bignum order; + FC_ASSERT( EC_GROUP_get_order( group, order, ctx ) ); + private_key_secret bin; + FC_ASSERT( BN_num_bytes( order ) == bin.data_size() ); + FC_ASSERT( BN_bn2bin( order, (unsigned char*) bin.data() ) == bin.data_size() ); + return bin; + } + + const private_key_secret& get_curve_order() + { + static private_key_secret order = _get_curve_order(); + return order; + } } public_key public_key::from_key_data( const public_key_data &data ) { @@ -246,6 +271,9 @@ namespace fc { namespace ecc { return extended_public_key( get_public_key(), c, child_num, parent_fp, depth ); } + public_key extended_public_key::generate_p(int i) const { return derive_normal_child(2*i + 0); } + public_key extended_public_key::generate_q(int i) const { return derive_normal_child(2*i + 1); } + extended_private_key extended_private_key::derive_child(int i) const { return i < 0 ? derive_hardened_child(i) : derive_normal_child(i); @@ -283,6 +311,11 @@ namespace fc { namespace ecc { return result; } + private_key extended_private_key::generate_a(int i) const { return derive_hardened_child(4*i + 0); } + private_key extended_private_key::generate_b(int i) const { return derive_hardened_child(4*i + 1); } + private_key extended_private_key::generate_c(int i) const { return derive_hardened_child(4*i + 2); } + private_key extended_private_key::generate_d(int i) const { return derive_hardened_child(4*i + 3); } + fc::string extended_private_key::str() const { return _to_base58( serialize_extended() ); diff --git a/src/crypto/elliptic_secp256k1.cpp b/src/crypto/elliptic_secp256k1.cpp index 70882ab..ee0b785 100644 --- a/src/crypto/elliptic_secp256k1.cpp +++ b/src/crypto/elliptic_secp256k1.cpp @@ -49,6 +49,8 @@ namespace fc { namespace ecc { chr37 _derive_message( const public_key_data& key, int i ); fc::sha256 _left( const fc::sha512& v ); fc::sha256 _right( const fc::sha512& v ); + const ec_group& get_curve(); + const private_key_secret& get_curve_order(); } static const public_key_data empty_pub; @@ -169,11 +171,130 @@ namespace fc { namespace ecc { const detail::chr37 data = detail::_derive_message( key, i ); fc::sha512 l = mac.digest( c.data(), c.data_size(), data.begin(), data.size() ); fc::sha256 left = detail::_left(l); + FC_ASSERT( left < detail::get_curve_order() ); FC_ASSERT( secp256k1_ec_pubkey_tweak_add( detail::_get_context(), (unsigned char*) key.begin(), key.size(), (unsigned char*) left.data() ) > 0 ); + // FIXME: check validity - if left + key == infinity then invalid extended_public_key result( key, detail::_right(l), i, fingerprint(), depth + 1 ); return result; } + static void to_bignum( const private_key_secret& in, ssl_bignum& out ) + { + if ( in.data()[0] & 0x80 ) + { + unsigned char buffer[33]; + *buffer = 0; + memcpy( buffer + 1, in.data(), 32 ); + BN_bin2bn( buffer, sizeof(buffer), out ); + } + else + { + BN_bin2bn( (unsigned char*) in.data(), in.data_size(), out ); + } + } + + 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 ); + } + } + + static void invert( const private_key_secret& in, private_key_secret& out ) + { + ssl_bignum bn_in; + to_bignum( in, bn_in ); + ssl_bignum bn_n; + to_bignum( detail::get_curve_order(), bn_n ); + ssl_bignum bn_inv; + bn_ctx ctx( BN_CTX_new() ); + FC_ASSERT( BN_mod_inverse( bn_inv, bn_in, bn_n, ctx ) ); + from_bignum( bn_inv, out ); + } + + static void to_point( const public_key_data& in, ec_point& out ) + { + bn_ctx ctx( BN_CTX_new() ); + const ec_group& curve = detail::get_curve(); + private_key_secret x; + memcpy( x.data(), in.begin() + 1, x.data_size() ); + ssl_bignum bn_x; + to_bignum( x, bn_x ); + FC_ASSERT( EC_POINT_set_compressed_coordinates_GFp( curve, out, bn_x, *in.begin() & 1, ctx ) > 0 ); + } + + static void from_point( const ec_point& in, public_key_data& out ) + { + bn_ctx ctx( BN_CTX_new() ); + const ec_group& curve = detail::get_curve(); + ssl_bignum bn_x; + ssl_bignum bn_y; + FC_ASSERT( EC_POINT_get_affine_coordinates_GFp( curve, in, bn_x, bn_y, ctx ) > 0 ); + private_key_secret x; + from_bignum( bn_x, x ); + memcpy( out.begin() + 1, x.data(), out.size() - 1 ); + *out.begin() = BN_is_bit_set( bn_y, 0 ) ? 3 : 2; + } + + static public_key compute_k( const private_key_secret& a, const private_key_secret& c, + const public_key& p ) + { + private_key_secret prod = a; + FC_ASSERT( secp256k1_ec_privkey_tweak_mul( detail::_get_context(), (unsigned char*) prod.data(), (unsigned char*) c.data() ) > 0 ); + invert( prod, prod ); + public_key_data P = p.serialize(); + FC_ASSERT( secp256k1_ec_pubkey_tweak_mul( detail::_get_context(), (unsigned char*) P.begin(), P.size(), (unsigned char*) prod.data() ) ); + return public_key( P ); + } + + 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 ) + { + private_key_secret prod; + invert( c, prod ); // prod == c^-1 + FC_ASSERT( secp256k1_ec_privkey_tweak_mul( detail::_get_context(), (unsigned char*) prod.data(), (unsigned char*) d.data() ) > 0 ); + // prod == c^-1 * a + + public_key_data accu = p; + FC_ASSERT( secp256k1_ec_pubkey_tweak_mul( detail::_get_context(), (unsigned char*) accu.begin(), accu.size(), (unsigned char*) prod.data() ) ); + // accu == prod * P == c^-1 * a * P + + ec_point point_accu( EC_POINT_new( detail::get_curve() ) ); + to_point( accu, point_accu ); + ec_point point_q( EC_POINT_new( detail::get_curve() ) ); + to_point( q, point_q ); + bn_ctx ctx(BN_CTX_new()); + FC_ASSERT( EC_POINT_add( detail::get_curve(), point_accu, point_accu, point_q, ctx ) > 0 ); + from_point( point_accu, accu ); + // accu == c^-1 * a * P + Q + + FC_ASSERT( secp256k1_ec_pubkey_tweak_add( detail::_get_context(), (unsigned char*) accu.begin(), accu.size(), (unsigned char*) b.data() ) ); + // accu == c^-1 * a * P + Q + b*G + + public_key_data k = compute_k( a, c, p ).serialize(); + memcpy( prod.data(), k.begin() + 1, prod.data_size() ); + // prod == Kx + FC_ASSERT( secp256k1_ec_privkey_tweak_mul( detail::_get_context(), (unsigned char*) prod.data(), (unsigned char*) a.data() ) > 0 ); + // prod == Kx * a + invert( prod, prod ); + // prod == (Kx * a)^-1 + + FC_ASSERT( secp256k1_ec_pubkey_tweak_mul( detail::_get_context(), (unsigned char*) accu.begin(), accu.size(), (unsigned char*) prod.data() ) ); + // accu == (c^-1 * a * P + Q + b*G) * (Kx * a)^-1 + + return public_key( accu ); + } + extended_private_key::extended_private_key( const private_key& k, const sha256& c, int child, int parent, uint8_t depth ) : private_key(k), c(c), child_num(child), parent_fp(parent), depth(depth) { } @@ -182,12 +303,76 @@ namespace fc { namespace ecc { int i) const { fc::sha256 left = detail::_left(hash); + FC_ASSERT( left < detail::get_curve_order() ); FC_ASSERT( secp256k1_ec_privkey_tweak_add( detail::_get_context(), (unsigned char*) left.data(), (unsigned char*) get_secret().data() ) > 0 ); extended_private_key result( private_key::regenerate( left ), detail::_right(hash), i, fingerprint(), depth + 1 ); return result; } + public_key extended_private_key::blind_public_key( const extended_public_key& bob, int i ) const + { + private_key_secret a = generate_a(i).get_secret(); + private_key_secret b = generate_b(i).get_secret(); + private_key_secret c = generate_c(i).get_secret(); + private_key_secret d = generate_d(i).get_secret(); + public_key_data p = bob.generate_p(i).serialize(); + public_key_data q = bob.generate_q(i).serialize(); + return compute_t( a, b, c, d, p, q ); + } + + blinded_hash extended_private_key::blind_hash( const fc::sha256& hash, int i ) const + { + private_key_secret a = generate_a(i).get_secret(); + private_key_secret b = generate_b(i).get_secret(); + FC_ASSERT( secp256k1_ec_privkey_tweak_mul( detail::_get_context(), (unsigned char*) a.data(), (unsigned char*) hash.data() ) > 0 ); + FC_ASSERT( secp256k1_ec_privkey_tweak_add( detail::_get_context(), (unsigned char*) a.data(), (unsigned char*) b.data() ) > 0 ); + return a; + } + + private_key_secret extended_private_key::compute_p( int i ) const + { + private_key_secret p_inv = derive_normal_child( 2*i ).get_secret(); + invert( p_inv, p_inv ); + return p_inv; + } + + private_key_secret extended_private_key::compute_q( int i, const private_key_secret& p ) const + { + private_key_secret q = derive_normal_child( 2*i + 1 ).get_secret(); + private_key_secret p_inv; + invert( p, p_inv ); + FC_ASSERT( secp256k1_ec_privkey_tweak_mul( detail::_get_context(), (unsigned char*) q.data(), (unsigned char*) p_inv.data() ) > 0 ); + return q; + } + + blind_signature extended_private_key::blind_sign( const blinded_hash& hash, int i ) const + { + private_key_secret p = compute_p( i ); + private_key_secret q = compute_q( i, p ); + FC_ASSERT( secp256k1_ec_privkey_tweak_mul( detail::_get_context(), (unsigned char*) p.data(), (unsigned char*) hash.data() ) > 0 ); + FC_ASSERT( secp256k1_ec_privkey_tweak_add( detail::_get_context(), (unsigned char*) p.data(), (unsigned char*) q.data() ) > 0 ); + return p; + } + + compact_signature extended_private_key::unblind_signature( const extended_public_key& bob, + const blind_signature& sig, + int i ) const + { + private_key_secret c = generate_c(i).get_secret(); + private_key_secret d = generate_d(i).get_secret(); + FC_ASSERT( secp256k1_ec_privkey_tweak_mul( detail::_get_context(), (unsigned char*) c.data(), (unsigned char*) sig.data() ) > 0 ); + FC_ASSERT( secp256k1_ec_privkey_tweak_add( detail::_get_context(), (unsigned char*) c.data(), (unsigned char*) d.data() ) > 0 ); + + private_key_secret a = generate_a(i).get_secret(); + public_key p = bob.generate_p(i); + 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 ); + return result; + } commitment_type blind( const blind_factor_type& blind, uint64_t value ) {