Initial blinding implementation, untested
This commit is contained in:
parent
5ecdcba4b6
commit
56e98e136a
4 changed files with 247 additions and 14 deletions
|
|
@ -25,6 +25,8 @@ namespace fc {
|
|||
typedef fc::array<unsigned char,65> compact_signature;
|
||||
typedef std::vector<char> range_proof_type;
|
||||
typedef fc::array<char,78> 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<commitment_type>& commits, const std::vector<commitment_type>& 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 );
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
#include <fc/crypto/elliptic.hpp>
|
||||
#include <fc/io/raw.hpp>
|
||||
#include <fc/crypto/hmac.hpp>
|
||||
#include <fc/crypto/openssl.hpp>
|
||||
#include <fc/crypto/ripemd160.hpp>
|
||||
|
||||
/* 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() );
|
||||
|
|
|
|||
|
|
@ -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 )
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in a new issue