Initial blinding implementation, untested

This commit is contained in:
Peter Conrad 2015-07-11 21:27:05 +02:00
parent 5ecdcba4b6
commit 56e98e136a
4 changed files with 247 additions and 14 deletions

View file

@ -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 );

View file

@ -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;
};

View file

@ -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() );

View file

@ -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 )
{