From 0575840a9c6ca3cf36731719f3f463c7eadfa373 Mon Sep 17 00:00:00 2001 From: Peter Conrad Date: Fri, 3 Jul 2015 21:48:28 +0200 Subject: [PATCH 1/6] Implemented HMAC template + test --- include/fc/crypto/hmac.hpp | 68 +++++++++++++++++++++ tests/hmac_test.cpp | 118 +++++++++++++++++++++++++++++++++++++ 2 files changed, 186 insertions(+) create mode 100644 include/fc/crypto/hmac.hpp create mode 100644 tests/hmac_test.cpp diff --git a/include/fc/crypto/hmac.hpp b/include/fc/crypto/hmac.hpp new file mode 100644 index 000000000..aa6d00675 --- /dev/null +++ b/include/fc/crypto/hmac.hpp @@ -0,0 +1,68 @@ +/* + * File: hmac.hpp + * Author: Peter Conrad + * + * Created on 1. Juli 2015, 21:48 + */ + +#ifndef HMAC_HPP +#define HMAC_HPP + +#include +#include +#include + +namespace fc { + + template + class hmac + { + public: + hmac() {} + + H digest( const char* c, uint32_t c_len, const char* d, uint32_t d_len ) + { + encoder.reset(); + add_key(c, c_len, 0x36); + encoder.write( d, d_len ); + H intermediate = encoder.result(); + + encoder.reset(); + add_key(c, c_len, 0x5c); + encoder.write( intermediate.data(), intermediate.data_size() ); + return encoder.result(); + } + + private: + void add_key( const char* c, const uint32_t c_len, char pad ) + { + if ( c_len > internal_block_size() ) + { + H hash = H::hash( c, c_len ); + add_key( hash.data(), hash.data_size(), pad ); + } + else + for (unsigned int i = 0; i < internal_block_size(); i++ ) + { + encoder.put( pad ^ ((i < c_len) ? *c++ : 0) ); + } + } + + unsigned int internal_block_size() const; + + H dummy; + typename H::encoder encoder; + }; + + template<> + unsigned int hmac::internal_block_size() const { return 64; } + + template<> + unsigned int hmac::internal_block_size() const { return 64; } + + template<> + unsigned int hmac::internal_block_size() const { return 128; } +} + +#endif /* HMAC_HPP */ + diff --git a/tests/hmac_test.cpp b/tests/hmac_test.cpp new file mode 100644 index 000000000..a8f21dd6d --- /dev/null +++ b/tests/hmac_test.cpp @@ -0,0 +1,118 @@ +#define BOOST_TEST_MODULE HmacTest +#include +#include +#include +#include +#include +#include +#include + +// See http://tools.ietf.org/html/rfc4231 + +static const fc::string TEST1_KEY = "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"; +static const fc::string TEST1_DATA = "4869205468657265"; +static const fc::string TEST1_224 = "896fb1128abbdf196832107cd49df33f47b4b1169912ba4f53684b22"; +static const fc::string TEST1_256 = "b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7"; +static const fc::string TEST1_512 = "87aa7cdea5ef619d4ff0b4241a1d6cb02379f4e2ce4ec2787ad0b30545e17cde" + "daa833b7d6b8a702038b274eaea3f4e4be9d914eeb61f1702e696c203a126854"; + +static const fc::string TEST2_KEY = "4a656665"; +static const fc::string TEST2_DATA = "7768617420646f2079612077616e7420666f72206e6f7468696e673f"; +static const fc::string TEST2_224 = "a30e01098bc6dbbf45690f3a7e9e6d0f8bbea2a39e6148008fd05e44"; +static const fc::string TEST2_256 = "5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843"; +static const fc::string TEST2_512 = "164b7a7bfcf819e2e395fbe73b56e0a387bd64222e831fd610270cd7ea250554" + "9758bf75c05a994a6d034f65f8f0e6fdcaeab1a34d4a6b4b636e070a38bce737"; + +static const fc::string TEST3_KEY = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; +static const fc::string TEST3_DATA = "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd" + "dddddddddddddddddddddddddddddddddddd"; +static const fc::string TEST3_224 = "7fb3cb3588c6c1f6ffa9694d7d6ad2649365b0c1f65d69d1ec8333ea"; +static const fc::string TEST3_256 = "773ea91e36800e46854db8ebd09181a72959098b3ef8c122d9635514ced565fe"; +static const fc::string TEST3_512 = "fa73b0089d56a284efb0f0756c890be9b1b5dbdd8ee81a3655f83e33b2279d39" + "bf3e848279a722c806b485a47e67c807b946a337bee8942674278859e13292fb"; + +static const fc::string TEST4_KEY = "0102030405060708090a0b0c0d0e0f10111213141516171819"; +static const fc::string TEST4_DATA = "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd" + "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd"; +static const fc::string TEST4_224 = "6c11506874013cac6a2abc1bb382627cec6a90d86efc012de7afec5a"; +static const fc::string TEST4_256 = "82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b"; +static const fc::string TEST4_512 = "b0ba465637458c6990e5a8c5f61d4af7e576d97ff94b872de76f8050361ee3db" + "a91ca5c11aa25eb4d679275cc5788063a5f19741120c4f2de2adebeb10a298dd"; + +// test 5 skipped - truncated + +static const fc::string TEST6_KEY = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaa"; +static const fc::string TEST6_DATA = "54657374205573696e67204c6172676572205468616e20426c6f636b2d53697a" + "65204b6579202d2048617368204b6579204669727374"; +static const fc::string TEST6_224 = "95e9a0db962095adaebe9b2d6f0dbce2d499f112f2d2b7273fa6870e"; +static const fc::string TEST6_256 = "60e431591ee0b67f0d8a26aacbf5b77f8e0bc6213728c5140546040f0ee37f54"; +static const fc::string TEST6_512 = "80b24263c7c1a3ebb71493c1dd7be8b49b46d1f41b4aeec1121b013783f8f352" + "6b56d037e05f2598bd0fd2215d6a1e5295e64f73f63f0aec8b915a985d786598"; + +static const fc::string TEST7_KEY = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaa"; +static const fc::string TEST7_DATA = "5468697320697320612074657374207573696e672061206c6172676572207468" + "616e20626c6f636b2d73697a65206b657920616e642061206c61726765722074" + "68616e20626c6f636b2d73697a6520646174612e20546865206b6579206e6565" + "647320746f20626520686173686564206265666f7265206265696e6720757365" + "642062792074686520484d414320616c676f726974686d2e"; +static const fc::string TEST7_224 = "3a854166ac5d9f023f54d517d0b39dbd946770db9c2b95c9f6f565d1"; +static const fc::string TEST7_256 = "9b09ffa71b942fcb27635fbcd5b0e944bfdc63644f0713938a7f51535c3a35e2"; +static const fc::string TEST7_512 = "e37b6a775dc87dbaa4dfa9f96e5e3ffddebd71f8867289865df5a32d20cdc944" + "b6022cac3c4982b10d5eeb55c3e4de15134676fb6de0446065c97440fa8c6a58"; + +static fc::hmac mac_224; +static fc::hmac mac_256; +static fc::hmac mac_512; + +template +static void run_test( const fc::string& key, const fc::string& data, const fc::string& expect_224, + const fc::string& expect_256, const fc::string& expect_512 ) +{ + + fc::array key_arr; + BOOST_CHECK_EQUAL( fc::from_hex( key, key_arr.begin(), key_arr.size() ), N ); + fc::array data_arr; + BOOST_CHECK_EQUAL( fc::from_hex( data, data_arr.begin(), data_arr.size() ), M ); + + BOOST_CHECK_EQUAL( mac_224.digest( key_arr.begin(), N, data_arr.begin(), M ).str(), expect_224 ); + BOOST_CHECK_EQUAL( mac_256.digest( key_arr.begin(), N, data_arr.begin(), M ).str(), expect_256 ); + BOOST_CHECK_EQUAL( mac_512.digest( key_arr.begin(), N, data_arr.begin(), M ).str(), expect_512 ); +} + +BOOST_AUTO_TEST_CASE(hmac_test_1) +{ + run_test<20,8>( TEST1_KEY, TEST1_DATA, TEST1_224, TEST1_256, TEST1_512 ); +} + +BOOST_AUTO_TEST_CASE(hmac_test_2) +{ + run_test<4,28>( TEST2_KEY, TEST2_DATA, TEST2_224, TEST2_256, TEST2_512 ); +} + +BOOST_AUTO_TEST_CASE(hmac_test_3) +{ + run_test<20,50>( TEST3_KEY, TEST3_DATA, TEST3_224, TEST3_256, TEST3_512 ); +} + +BOOST_AUTO_TEST_CASE(hmac_test_4) +{ + run_test<25,50>( TEST4_KEY, TEST4_DATA, TEST4_224, TEST4_256, TEST4_512 ); +} + +BOOST_AUTO_TEST_CASE(hmac_test_6) +{ + run_test<131,54>( TEST6_KEY, TEST6_DATA, TEST6_224, TEST6_256, TEST6_512 ); +} + +BOOST_AUTO_TEST_CASE(hmac_test_7) +{ + run_test<131,152>( TEST7_KEY, TEST7_DATA, TEST7_224, TEST7_256, TEST7_512 ); +} From 0f67ca751eaa2fb116f96cf8942e8da1cc515a8e Mon Sep 17 00:00:00 2001 From: Peter Conrad Date: Sat, 4 Jul 2015 18:48:21 +0200 Subject: [PATCH 2/6] Implemented extended keys --- CMakeLists.txt | 6 + include/fc/crypto/elliptic.hpp | 53 +++++++ src/crypto/elliptic_common.cpp | 9 ++ src/crypto/elliptic_secp256k1.cpp | 227 ++++++++++++++++++++++++++++++ tests/blinding_test.cpp | 105 ++++++++++++++ 5 files changed, 400 insertions(+) create mode 100644 tests/blinding_test.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index a95eff3cf..0e8cbb6e5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -341,6 +341,12 @@ target_link_libraries( bloom_test fc ) add_executable( real128_test tests/all_tests.cpp tests/real128_test.cpp ) target_link_libraries( real128_test fc ) +add_executable( hmac_test tests/hmac_test.cpp ) +target_link_libraries( hmac_test fc ) + +add_executable( blinding_test tests/blinding_test.cpp ) +target_link_libraries( blinding_test fc ) + add_executable( udt_server tests/udts.cpp ) target_link_libraries( udt_server fc udt ) diff --git a/include/fc/crypto/elliptic.hpp b/include/fc/crypto/elliptic.hpp index b05dc5822..600d7b346 100644 --- a/include/fc/crypto/elliptic.hpp +++ b/include/fc/crypto/elliptic.hpp @@ -24,6 +24,7 @@ namespace fc { typedef fc::array signature; typedef fc::array compact_signature; typedef std::vector range_proof_type; + typedef fc::array extended_key_data; /** * @class public_key @@ -73,6 +74,8 @@ namespace fc { static std::string to_base58( const public_key_data &key ); static public_key from_base58( const std::string& b58 ); + unsigned int fingerprint() const; + private: friend class private_key; static public_key from_key_data( const public_key_data& v ); @@ -136,13 +139,63 @@ namespace fc { return a.get_secret() < b.get_secret(); } + unsigned int fingerprint() const { return get_public_key().fingerprint(); } + private: private_key( EC_KEY* k ); static fc::sha256 get_secret( const EC_KEY * const k ); fc::fwd my; }; + class extended_public_key : public public_key + { + public: + extended_public_key( const public_key& k, const sha256& c, + int child = 0, int parent_fp = 0, uint8_t depth = 0 ); + + extended_public_key derive_child( int i ) const; + extended_public_key derive_normal_child( int i ) const; + + extended_key_data serialize_extended() const; + static extended_public_key deserialize( const extended_key_data& data ); + fc::string str() const; + fc::string to_base58() const { return str(); } + static extended_public_key from_base58( const fc::string& base58 ); + + private: + sha256 c; + int child_num, parent_fp; + uint8_t depth; + }; + + class extended_private_key : public private_key + { + public: + extended_private_key( const private_key& k, const sha256& c, + int child = 0, int parent_fp = 0, uint8_t depth = 0 ); + + extended_public_key get_extended_public_key()const; + + extended_private_key derive_child( int i ) const; + extended_private_key derive_normal_child( int i ) const; + extended_private_key derive_hardened_child( int i ) const; + extended_key_data serialize_extended() const; + static extended_private_key deserialize( const extended_key_data& data ); + fc::string str() const; + fc::string to_base58() const { return str(); } + static extended_private_key from_base58( const fc::string& base58 ); + static extended_private_key generate_master( const fc::string& seed ); + static extended_private_key generate_master( const char* seed, uint32_t seed_len ); + + private: + extended_private_key private_derive_rest( const fc::sha512& hash, + int num ) const; + + sha256 c; + int child_num, parent_fp; + uint8_t depth; + }; struct range_proof_info { diff --git a/src/crypto/elliptic_common.cpp b/src/crypto/elliptic_common.cpp index a66c26b27..cd10b145a 100644 --- a/src/crypto/elliptic_common.cpp +++ b/src/crypto/elliptic_common.cpp @@ -1,6 +1,7 @@ #include #include #include +#include /* stuff common to all ecc implementations */ @@ -50,6 +51,14 @@ namespace fc { namespace ecc { return from_key_data(key); } + unsigned int public_key::fingerprint() const + { + public_key_data key = serialize(); + ripemd160 hash = ripemd160::hash( sha256::hash( key.begin(), key.size() ) ); + unsigned char* fp = (unsigned char*) hash._hash; + return (fp[0] << 24) | (fp[1] << 16) | (fp[2] << 8) | fp[3]; + } + bool public_key::is_canonical( const compact_signature& c ) { return !(c.data[1] & 0x80) && !(c.data[1] == 0 && !(c.data[2] & 0x80)) diff --git a/src/crypto/elliptic_secp256k1.cpp b/src/crypto/elliptic_secp256k1.cpp index 37cf8cd29..54616e5fc 100644 --- a/src/crypto/elliptic_secp256k1.cpp +++ b/src/crypto/elliptic_secp256k1.cpp @@ -1,7 +1,9 @@ #include #include +#include #include +#include #include #include @@ -12,6 +14,9 @@ #include "_elliptic_impl_priv.hpp" +#define BTC_EXT_PUB_MAGIC (0x0488B21E) +#define BTC_EXT_PRIV_MAGIC (0x0488ADE4) + namespace fc { namespace ecc { namespace detail { @@ -151,7 +156,229 @@ namespace fc { namespace ecc { FC_ASSERT( pk_len == my->_key.size() ); } + static fc::sha256 _left( const fc::sha512& v ) + { + fc::sha256 result; + memcpy( result.data(), v.data(), 32 ); + return result; + } + + static fc::sha256 _right( const fc::sha512& v ) + { + fc::sha256 result; + memcpy( result.data(), v.data() + 32, 32 ); + return result; + } + + typedef fc::array chr37; + + static void _put( unsigned char** dest, unsigned int i) + { + *(*dest)++ = (i >> 24) & 0xff; + *(*dest)++ = (i >> 16) & 0xff; + *(*dest)++ = (i >> 8) & 0xff; + *(*dest)++ = i & 0xff; + } + + static unsigned int _get( unsigned char** src ) + { + unsigned int result = *(*src)++ << 24; + result |= *(*src)++ << 16; + result |= *(*src)++ << 8; + result |= *(*src)++; + return result; + } + + static chr37 _derive_message( char first, const char* key32, int i ) + { + chr37 result; + unsigned char* dest = (unsigned char*) result.begin(); + *dest++ = first; + memcpy( dest, key32, 32 ); dest += 32; + _put( &dest, i ); + return result; + } + + static chr37 _derive_message( const public_key_data& key, int i ) + { + return _derive_message( *key.begin(), key.begin() + 1, i ); + } + + static chr37 _derive_message( const private_key_secret& key, int i ) + { + return _derive_message( 0, key.data(), i ); + } + + static fc::string _to_base58( const extended_key_data& key ) + { + char buffer[key.size() + 4]; + memcpy( buffer, key.begin(), key.size() ); + fc::sha256 double_hash = fc::sha256::hash( fc::sha256::hash( key.begin(), key.size() )); + memcpy( buffer + key.size(), double_hash.data(), 4 ); + return fc::to_base58( buffer, sizeof(buffer) ); + } + + static void _parse_extended_data( unsigned char* buffer, fc::string base58 ) + { + memset( buffer, 0, 78 ); + std::vector decoded = fc::from_base58( base58 ); + unsigned int i = 0; + for ( char c : decoded ) + { + if ( i >= 78 || i > decoded.size() - 4 ) { break; } + buffer[i++] = c; + } + } + + typedef hmac hmac_sha512; + + extended_public_key::extended_public_key( const public_key& k, const fc::sha256& c, + int child, int parent, uint8_t depth ) + : public_key(k), c(c), child_num(child), parent_fp(parent), depth(depth) { } + + extended_public_key extended_public_key::derive_child(int i) const + { + FC_ASSERT( !(i&0x80000000), "Can't derive hardened public key!" ); + return derive_normal_child(i); + } + + extended_public_key extended_public_key::derive_normal_child(int i) const + { + hmac_sha512 mac; + public_key_data key = serialize(); + const chr37 data = _derive_message( key, i ); + fc::sha512 l = mac.digest( c.data(), c.data_size(), data.begin(), data.size() ); + fc::sha256 left = _left(l); + FC_ASSERT( secp256k1_ec_pubkey_tweak_add( detail::_get_context(), (unsigned char*) key.begin(), key.size(), (unsigned char*) left.data() ) > 0 ); + extended_public_key result( key, _right(l), i, fingerprint(), depth + 1 ); + return result; + } + + extended_key_data extended_public_key::serialize_extended() const + { + extended_key_data result; + unsigned char* dest = (unsigned char*) result.begin(); + _put( &dest, BTC_EXT_PUB_MAGIC ); + *dest++ = depth; + _put( &dest, parent_fp ); + _put( &dest, child_num ); + memcpy( dest, c.data(), c.data_size() ); dest += 32; + public_key_data key = serialize(); + memcpy( dest, key.begin(), key.size() ); + return result; + } + + fc::string extended_public_key::str() const + { + return _to_base58( serialize_extended() ); + } + + extended_public_key extended_public_key::from_base58( const fc::string& base58 ) + { + unsigned char buffer[78]; + unsigned char* ptr = buffer; + _parse_extended_data( buffer, base58 ); + FC_ASSERT( _get( &ptr ) == BTC_EXT_PUB_MAGIC, "Invalid extended private key" ); + uint8_t d = *ptr++; + int fp = _get( &ptr ); + int cn = _get( &ptr ); + fc::sha256 chain; + memcpy( chain.data(), ptr, chain.data_size() ); ptr += chain.data_size(); + public_key_data key; + memcpy( key.begin(), ptr, key.size() ); + return extended_public_key( key, chain, cn, fp, d ); + } + + 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) { } + extended_public_key extended_private_key::get_extended_public_key() const + { + return extended_public_key( get_public_key(), c, child_num, parent_fp, depth ); + } + + extended_private_key extended_private_key::derive_child(int i) const + { + return i < 0 ? derive_hardened_child(i) : derive_normal_child(i); + } + + extended_private_key extended_private_key::derive_normal_child(int i) const + { + const chr37 data = _derive_message( get_public_key().serialize(), i ); + hmac_sha512 mac; + fc::sha512 l = mac.digest( c.data(), c.data_size(), data.begin(), data.size() ); + return private_derive_rest( l, i ); + } + + extended_private_key extended_private_key::derive_hardened_child(int i) const + { + hmac_sha512 mac; + private_key_secret key = get_secret(); + const chr37 data = _derive_message( key, i ); + fc::sha512 l = mac.digest( c.data(), c.data_size(), data.begin(), data.size() ); + return private_derive_rest( l, i ); + } + + extended_private_key extended_private_key::private_derive_rest( const fc::sha512& hash, + int i) const + { + fc::sha256 left = _left(hash); + 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 ), _right(hash), + i, fingerprint(), depth + 1 ); + return result; + } + + extended_key_data extended_private_key::serialize_extended() const + { + extended_key_data result; + unsigned char* dest = (unsigned char*) result.begin(); + _put( &dest, BTC_EXT_PRIV_MAGIC ); + *dest++ = depth; + _put( &dest, parent_fp ); + _put( &dest, child_num ); + memcpy( dest, c.data(), c.data_size() ); dest += 32; + *dest++ = 0; + private_key_secret key = get_secret(); + memcpy( dest, key.data(), key.data_size() ); + return result; + } + + fc::string extended_private_key::str() const + { + return _to_base58( serialize_extended() ); + } + + extended_private_key extended_private_key::from_base58( const fc::string& base58 ) + { + unsigned char buffer[78]; + unsigned char* ptr = buffer; + _parse_extended_data( buffer, base58 ); + FC_ASSERT( _get( &ptr ) == BTC_EXT_PRIV_MAGIC, "Invalid extended private key" ); + uint8_t d = *ptr++; + int fp = _get( &ptr ); + int cn = _get( &ptr ); + fc::sha256 chain; + memcpy( chain.data(), ptr, chain.data_size() ); ptr += chain.data_size(); + ptr++; + private_key_secret key; + memcpy( key.data(), ptr, key.data_size() ); + return extended_private_key( private_key::regenerate(key), chain, cn, fp, d ); + } + + extended_private_key extended_private_key::generate_master( const fc::string& seed ) + { + return generate_master( seed.c_str(), seed.size() ); + } + + extended_private_key extended_private_key::generate_master( const char* seed, uint32_t seed_len ) + { + hmac_sha512 mac; + fc::sha512 hash = mac.digest( "Bitcoin seed", 12, seed, seed_len ); + extended_private_key result( private_key::regenerate( _left(hash) ), _right(hash) ); + return result; + } commitment_type blind( const blind_factor_type& blind, uint64_t value ) diff --git a/tests/blinding_test.cpp b/tests/blinding_test.cpp new file mode 100644 index 000000000..2b641674d --- /dev/null +++ b/tests/blinding_test.cpp @@ -0,0 +1,105 @@ +#define BOOST_TEST_MODULE BlindingTest +#include +#include +#include +#include +#include + +// See https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#Test_Vectors + +static fc::string TEST1_SEED = "000102030405060708090a0b0c0d0e0f"; +static fc::string TEST1_M_PUB = "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8"; +static fc::string TEST1_M_PRIV = "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi"; +static fc::string TEST1_M_0H_PUB = "xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw"; +static fc::string TEST1_M_0H_PRIV = "xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7"; +static fc::string TEST1_M_0H_1_PUB = "xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ"; +static fc::string TEST1_M_0H_1_PRIV = "xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs"; +static fc::string TEST1_M_0H_1_2H_PUB = "xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5"; +static fc::string TEST1_M_0H_1_2H_PRIV = "xprv9z4pot5VBttmtdRTWfWQmoH1taj2axGVzFqSb8C9xaxKymcFzXBDptWmT7FwuEzG3ryjH4ktypQSAewRiNMjANTtpgP4mLTj34bhnZX7UiM"; +static fc::string TEST1_M_0H_1_2H_2_PUB = "xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV"; +static fc::string TEST1_M_0H_1_2H_2_PRIV = "xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334"; +static fc::string TEST1_M_0H_1_2H_2_1g_PUB = "xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy"; +static fc::string TEST1_M_0H_1_2H_2_1g_PRIV = "xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76"; + +static fc::string TEST2_SEED = "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542"; +static fc::string TEST2_M_PUB = "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB"; +static fc::string TEST2_M_PRIV = "xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U"; +static fc::string TEST2_M_0_PUB = "xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH"; +static fc::string TEST2_M_0_PRIV = "xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt"; +static fc::string TEST2_M_0_m1_PUB = "xpub6ASAVgeehLbnwdqV6UKMHVzgqAG8Gr6riv3Fxxpj8ksbH9ebxaEyBLZ85ySDhKiLDBrQSARLq1uNRts8RuJiHjaDMBU4Zn9h8LZNnBC5y4a"; +static fc::string TEST2_M_0_m1_PRIV = "xprv9wSp6B7kry3Vj9m1zSnLvN3xH8RdsPP1Mh7fAaR7aRLcQMKTR2vidYEeEg2mUCTAwCd6vnxVrcjfy2kRgVsFawNzmjuHc2YmYRmagcEPdU9"; +static fc::string TEST2_M_0_m1_1_PUB = "xpub6DF8uhdarytz3FWdA8TvFSvvAh8dP3283MY7p2V4SeE2wyWmG5mg5EwVvmdMVCQcoNJxGoWaU9DCWh89LojfZ537wTfunKau47EL2dhHKon"; +static fc::string TEST2_M_0_m1_1_PRIV = "xprv9zFnWC6h2cLgpmSA46vutJzBcfJ8yaJGg8cX1e5StJh45BBciYTRXSd25UEPVuesF9yog62tGAQtHjXajPPdbRCHuWS6T8XA2ECKADdw4Ef"; +static fc::string TEST2_M_0_m1_1_m2_PUB = "xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL"; +static fc::string TEST2_M_0_m1_1_m2_PRIV = "xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc"; +static fc::string TEST2_M_0_m1_1_m2_2_PUB = "xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt"; +static fc::string TEST2_M_0_m1_1_m2_2_PRIV = "xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j"; + +BOOST_AUTO_TEST_CASE(test_extended_keys_1) +{ + char seed[16]; + fc::from_hex( TEST1_SEED, seed, sizeof(seed) ); + fc::ecc::extended_private_key master = fc::ecc::extended_private_key::generate_master( seed, sizeof(seed) ); + BOOST_CHECK_EQUAL( master.str(), TEST1_M_PRIV ); + BOOST_CHECK_EQUAL( master.get_extended_public_key().str(), TEST1_M_PUB ); + + BOOST_CHECK_EQUAL( fc::ecc::extended_private_key::from_base58(TEST1_M_PRIV).str(), TEST1_M_PRIV ); + BOOST_CHECK_EQUAL( fc::ecc::extended_public_key::from_base58(TEST1_M_PUB).str(), TEST1_M_PUB ); + BOOST_CHECK_EQUAL( fc::ecc::extended_private_key::from_base58(TEST1_M_0H_PRIV).str(), TEST1_M_0H_PRIV ); + BOOST_CHECK_EQUAL( fc::ecc::extended_public_key::from_base58(TEST1_M_0H_PUB).str(), TEST1_M_0H_PUB ); + + fc::ecc::extended_private_key m_0 = master.derive_child(0x80000000); + BOOST_CHECK_EQUAL( m_0.str(), TEST1_M_0H_PRIV ); + BOOST_CHECK_EQUAL( m_0.get_extended_public_key().str(), TEST1_M_0H_PUB ); + + fc::ecc::extended_private_key m_0_1 = m_0.derive_child(1); + BOOST_CHECK_EQUAL( m_0_1.str(), TEST1_M_0H_1_PRIV ); + BOOST_CHECK_EQUAL( m_0_1.get_extended_public_key().str(), TEST1_M_0H_1_PUB ); + BOOST_CHECK_EQUAL( m_0.get_extended_public_key().derive_child(1).str(), TEST1_M_0H_1_PUB ); + + fc::ecc::extended_private_key m_0_1_2 = m_0_1.derive_child(0x80000002); + BOOST_CHECK_EQUAL( m_0_1_2.str(), TEST1_M_0H_1_2H_PRIV ); + BOOST_CHECK_EQUAL( m_0_1_2.get_extended_public_key().str(), TEST1_M_0H_1_2H_PUB ); + + fc::ecc::extended_private_key m_0_1_2_2 = m_0_1_2.derive_child(2); + BOOST_CHECK_EQUAL( m_0_1_2_2.str(), TEST1_M_0H_1_2H_2_PRIV ); + BOOST_CHECK_EQUAL( m_0_1_2_2.get_extended_public_key().str(), TEST1_M_0H_1_2H_2_PUB ); + BOOST_CHECK_EQUAL( m_0_1_2.get_extended_public_key().derive_child(2).str(), TEST1_M_0H_1_2H_2_PUB ); + + fc::ecc::extended_private_key m_0_1_2_2_1g = m_0_1_2_2.derive_child(1000000000); + BOOST_CHECK_EQUAL( m_0_1_2_2_1g.str(), TEST1_M_0H_1_2H_2_1g_PRIV ); + BOOST_CHECK_EQUAL( m_0_1_2_2_1g.get_extended_public_key().str(), TEST1_M_0H_1_2H_2_1g_PUB ); + BOOST_CHECK_EQUAL( m_0_1_2_2.get_extended_public_key().derive_child(1000000000).str(), TEST1_M_0H_1_2H_2_1g_PUB ); +} + +BOOST_AUTO_TEST_CASE(test_extended_keys_2) +{ + char seed[64]; + fc::from_hex( TEST2_SEED, seed, sizeof(seed) ); + fc::ecc::extended_private_key master = fc::ecc::extended_private_key::generate_master( seed, sizeof(seed) ); + BOOST_CHECK_EQUAL( master.str(), TEST2_M_PRIV ); + BOOST_CHECK_EQUAL( master.get_extended_public_key().str(), TEST2_M_PUB ); + + fc::ecc::extended_private_key m_0 = master.derive_child(0); + BOOST_CHECK_EQUAL( m_0.str(), TEST2_M_0_PRIV ); + BOOST_CHECK_EQUAL( m_0.get_extended_public_key().str(), TEST2_M_0_PUB ); + BOOST_CHECK_EQUAL( master.get_extended_public_key().derive_child(0).str(), TEST2_M_0_PUB ); + + fc::ecc::extended_private_key m_0_m1 = m_0.derive_child(-1); + BOOST_CHECK_EQUAL( m_0_m1.str(), TEST2_M_0_m1_PRIV ); + BOOST_CHECK_EQUAL( m_0_m1.get_extended_public_key().str(), TEST2_M_0_m1_PUB ); + + fc::ecc::extended_private_key m_0_m1_1 = m_0_m1.derive_child(1); + BOOST_CHECK_EQUAL( m_0_m1_1.str(), TEST2_M_0_m1_1_PRIV ); + BOOST_CHECK_EQUAL( m_0_m1_1.get_extended_public_key().str(), TEST2_M_0_m1_1_PUB ); + BOOST_CHECK_EQUAL( m_0_m1.get_extended_public_key().derive_child(1).str(), TEST2_M_0_m1_1_PUB ); + + fc::ecc::extended_private_key m_0_m1_1_m2 = m_0_m1_1.derive_child(-2); + BOOST_CHECK_EQUAL( m_0_m1_1_m2.str(), TEST2_M_0_m1_1_m2_PRIV ); + BOOST_CHECK_EQUAL( m_0_m1_1_m2.get_extended_public_key().str(), TEST2_M_0_m1_1_m2_PUB ); + + fc::ecc::extended_private_key m_0_m1_1_m2_2 = m_0_m1_1_m2.derive_child(2); + BOOST_CHECK_EQUAL( m_0_m1_1_m2_2.str(), TEST2_M_0_m1_1_m2_2_PRIV ); + 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 ); +} From 5ecdcba4b689bd02b31f646cc2b3f5678361a944 Mon Sep 17 00:00:00 2001 From: Peter Conrad Date: Sun, 5 Jul 2015 14:19:17 +0200 Subject: [PATCH 3/6] Some refactoring --- include/fc/crypto/hmac.hpp | 11 +- src/crypto/elliptic_common.cpp | 198 ++++++++++++++++++++++++++++ src/crypto/elliptic_secp256k1.cpp | 212 ++---------------------------- src/crypto/sha224.cpp | 4 + src/crypto/sha256.cpp | 3 + src/crypto/sha512.cpp | 4 + 6 files changed, 222 insertions(+), 210 deletions(-) diff --git a/include/fc/crypto/hmac.hpp b/include/fc/crypto/hmac.hpp index aa6d00675..d1a18c62d 100644 --- a/include/fc/crypto/hmac.hpp +++ b/include/fc/crypto/hmac.hpp @@ -54,14 +54,9 @@ namespace fc { typename H::encoder encoder; }; - template<> - unsigned int hmac::internal_block_size() const { return 64; } - - template<> - unsigned int hmac::internal_block_size() const { return 64; } - - template<> - unsigned int hmac::internal_block_size() const { return 128; } + typedef hmac hmac_sha224; + typedef hmac hmac_sha256; + typedef hmac hmac_sha512; } #endif /* HMAC_HPP */ diff --git a/src/crypto/elliptic_common.cpp b/src/crypto/elliptic_common.cpp index cd10b145a..6de620d4e 100644 --- a/src/crypto/elliptic_common.cpp +++ b/src/crypto/elliptic_common.cpp @@ -1,12 +1,71 @@ #include #include #include +#include #include /* stuff common to all ecc implementations */ +#define BTC_EXT_PUB_MAGIC (0x0488B21E) +#define BTC_EXT_PRIV_MAGIC (0x0488ADE4) + namespace fc { namespace ecc { + namespace detail { + typedef fc::array chr37; + + fc::sha256 _left( const fc::sha512& v ) + { + fc::sha256 result; + memcpy( result.data(), v.data(), 32 ); + return result; + } + + fc::sha256 _right( const fc::sha512& v ) + { + fc::sha256 result; + memcpy( result.data(), v.data() + 32, 32 ); + return result; + } + + static void _put( unsigned char** dest, unsigned int i) + { + *(*dest)++ = (i >> 24) & 0xff; + *(*dest)++ = (i >> 16) & 0xff; + *(*dest)++ = (i >> 8) & 0xff; + *(*dest)++ = i & 0xff; + } + + static unsigned int _get( unsigned char** src ) + { + unsigned int result = *(*src)++ << 24; + result |= *(*src)++ << 16; + result |= *(*src)++ << 8; + result |= *(*src)++; + return result; + } + + static chr37 _derive_message( char first, const char* key32, int i ) + { + chr37 result; + unsigned char* dest = (unsigned char*) result.begin(); + *dest++ = first; + memcpy( dest, key32, 32 ); dest += 32; + _put( &dest, i ); + return result; + } + + chr37 _derive_message( const public_key_data& key, int i ) + { + return _derive_message( *key.begin(), key.begin() + 1, i ); + } + + static chr37 _derive_message( const private_key_secret& key, int i ) + { + return _derive_message( 0, key.data(), i ); + } + } + public_key public_key::from_key_data( const public_key_data &data ) { return public_key(data); } @@ -120,6 +179,145 @@ namespace fc { namespace ecc { return private_key( k ); } + static fc::string _to_base58( const extended_key_data& key ) + { + char buffer[key.size() + 4]; + memcpy( buffer, key.begin(), key.size() ); + fc::sha256 double_hash = fc::sha256::hash( fc::sha256::hash( key.begin(), key.size() )); + memcpy( buffer + key.size(), double_hash.data(), 4 ); + return fc::to_base58( buffer, sizeof(buffer) ); + } + + static void _parse_extended_data( unsigned char* buffer, fc::string base58 ) + { + memset( buffer, 0, 78 ); + std::vector decoded = fc::from_base58( base58 ); + unsigned int i = 0; + for ( char c : decoded ) + { + if ( i >= 78 || i > decoded.size() - 4 ) { break; } + buffer[i++] = c; + } + } + + extended_public_key extended_public_key::derive_child(int i) const + { + FC_ASSERT( !(i&0x80000000), "Can't derive hardened public key!" ); + return derive_normal_child(i); + } + + extended_key_data extended_public_key::serialize_extended() const + { + extended_key_data result; + unsigned char* dest = (unsigned char*) result.begin(); + detail::_put( &dest, BTC_EXT_PUB_MAGIC ); + *dest++ = depth; + detail::_put( &dest, parent_fp ); + detail::_put( &dest, child_num ); + memcpy( dest, c.data(), c.data_size() ); dest += 32; + public_key_data key = serialize(); + memcpy( dest, key.begin(), key.size() ); + return result; + } + + fc::string extended_public_key::str() const + { + return _to_base58( serialize_extended() ); + } + + extended_public_key extended_public_key::from_base58( const fc::string& base58 ) + { + unsigned char buffer[78]; + unsigned char* ptr = buffer; + _parse_extended_data( buffer, base58 ); + FC_ASSERT( detail::_get( &ptr ) == BTC_EXT_PUB_MAGIC, "Invalid extended private key" ); + uint8_t d = *ptr++; + int fp = detail::_get( &ptr ); + int cn = detail::_get( &ptr ); + fc::sha256 chain; + memcpy( chain.data(), ptr, chain.data_size() ); ptr += chain.data_size(); + public_key_data key; + memcpy( key.begin(), ptr, key.size() ); + return extended_public_key( key, chain, cn, fp, d ); + } + + extended_public_key extended_private_key::get_extended_public_key() const + { + return extended_public_key( get_public_key(), c, child_num, parent_fp, depth ); + } + + extended_private_key extended_private_key::derive_child(int i) const + { + return i < 0 ? derive_hardened_child(i) : derive_normal_child(i); + } + + extended_private_key extended_private_key::derive_normal_child(int i) const + { + const detail::chr37 data = detail::_derive_message( get_public_key().serialize(), i ); + hmac_sha512 mac; + fc::sha512 l = mac.digest( c.data(), c.data_size(), data.begin(), data.size() ); + return private_derive_rest( l, i ); + } + + extended_private_key extended_private_key::derive_hardened_child(int i) const + { + hmac_sha512 mac; + private_key_secret key = get_secret(); + const detail::chr37 data = detail::_derive_message( key, i ); + fc::sha512 l = mac.digest( c.data(), c.data_size(), data.begin(), data.size() ); + return private_derive_rest( l, i ); + } + + extended_key_data extended_private_key::serialize_extended() const + { + extended_key_data result; + unsigned char* dest = (unsigned char*) result.begin(); + detail::_put( &dest, BTC_EXT_PRIV_MAGIC ); + *dest++ = depth; + detail::_put( &dest, parent_fp ); + detail::_put( &dest, child_num ); + memcpy( dest, c.data(), c.data_size() ); dest += 32; + *dest++ = 0; + private_key_secret key = get_secret(); + memcpy( dest, key.data(), key.data_size() ); + return result; + } + + fc::string extended_private_key::str() const + { + return _to_base58( serialize_extended() ); + } + + extended_private_key extended_private_key::from_base58( const fc::string& base58 ) + { + unsigned char buffer[78]; + unsigned char* ptr = buffer; + _parse_extended_data( buffer, base58 ); + FC_ASSERT( detail::_get( &ptr ) == BTC_EXT_PRIV_MAGIC, "Invalid extended private key" ); + uint8_t d = *ptr++; + int fp = detail::_get( &ptr ); + int cn = detail::_get( &ptr ); + fc::sha256 chain; + memcpy( chain.data(), ptr, chain.data_size() ); ptr += chain.data_size(); + ptr++; + private_key_secret key; + memcpy( key.data(), ptr, key.data_size() ); + return extended_private_key( private_key::regenerate(key), chain, cn, fp, d ); + } + + extended_private_key extended_private_key::generate_master( const fc::string& seed ) + { + return generate_master( seed.c_str(), seed.size() ); + } + + extended_private_key extended_private_key::generate_master( const char* seed, uint32_t seed_len ) + { + hmac_sha512 mac; + fc::sha512 hash = mac.digest( "Bitcoin seed", 12, seed, seed_len ); + extended_private_key result( private_key::regenerate( detail::_left(hash) ), + detail::_right(hash) ); + return result; + } } void to_variant( const ecc::private_key& var, variant& vo ) diff --git a/src/crypto/elliptic_secp256k1.cpp b/src/crypto/elliptic_secp256k1.cpp index 54616e5fc..70882ab62 100644 --- a/src/crypto/elliptic_secp256k1.cpp +++ b/src/crypto/elliptic_secp256k1.cpp @@ -14,9 +14,6 @@ #include "_elliptic_impl_priv.hpp" -#define BTC_EXT_PUB_MAGIC (0x0488B21E) -#define BTC_EXT_PRIV_MAGIC (0x0488ADE4) - namespace fc { namespace ecc { namespace detail { @@ -47,6 +44,11 @@ namespace fc { namespace ecc { public_key_data _key; }; + + typedef fc::array chr37; + chr37 _derive_message( const public_key_data& key, int i ); + fc::sha256 _left( const fc::sha512& v ); + fc::sha256 _right( const fc::sha512& v ); } static const public_key_data empty_pub; @@ -156,230 +158,36 @@ namespace fc { namespace ecc { FC_ASSERT( pk_len == my->_key.size() ); } - static fc::sha256 _left( const fc::sha512& v ) - { - fc::sha256 result; - memcpy( result.data(), v.data(), 32 ); - return result; - } - - static fc::sha256 _right( const fc::sha512& v ) - { - fc::sha256 result; - memcpy( result.data(), v.data() + 32, 32 ); - return result; - } - - typedef fc::array chr37; - - static void _put( unsigned char** dest, unsigned int i) - { - *(*dest)++ = (i >> 24) & 0xff; - *(*dest)++ = (i >> 16) & 0xff; - *(*dest)++ = (i >> 8) & 0xff; - *(*dest)++ = i & 0xff; - } - - static unsigned int _get( unsigned char** src ) - { - unsigned int result = *(*src)++ << 24; - result |= *(*src)++ << 16; - result |= *(*src)++ << 8; - result |= *(*src)++; - return result; - } - - static chr37 _derive_message( char first, const char* key32, int i ) - { - chr37 result; - unsigned char* dest = (unsigned char*) result.begin(); - *dest++ = first; - memcpy( dest, key32, 32 ); dest += 32; - _put( &dest, i ); - return result; - } - - static chr37 _derive_message( const public_key_data& key, int i ) - { - return _derive_message( *key.begin(), key.begin() + 1, i ); - } - - static chr37 _derive_message( const private_key_secret& key, int i ) - { - return _derive_message( 0, key.data(), i ); - } - - static fc::string _to_base58( const extended_key_data& key ) - { - char buffer[key.size() + 4]; - memcpy( buffer, key.begin(), key.size() ); - fc::sha256 double_hash = fc::sha256::hash( fc::sha256::hash( key.begin(), key.size() )); - memcpy( buffer + key.size(), double_hash.data(), 4 ); - return fc::to_base58( buffer, sizeof(buffer) ); - } - - static void _parse_extended_data( unsigned char* buffer, fc::string base58 ) - { - memset( buffer, 0, 78 ); - std::vector decoded = fc::from_base58( base58 ); - unsigned int i = 0; - for ( char c : decoded ) - { - if ( i >= 78 || i > decoded.size() - 4 ) { break; } - buffer[i++] = c; - } - } - - typedef hmac hmac_sha512; - extended_public_key::extended_public_key( const public_key& k, const fc::sha256& c, int child, int parent, uint8_t depth ) : public_key(k), c(c), child_num(child), parent_fp(parent), depth(depth) { } - extended_public_key extended_public_key::derive_child(int i) const - { - FC_ASSERT( !(i&0x80000000), "Can't derive hardened public key!" ); - return derive_normal_child(i); - } - extended_public_key extended_public_key::derive_normal_child(int i) const { hmac_sha512 mac; public_key_data key = serialize(); - const chr37 data = _derive_message( key, i ); + 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 = _left(l); + fc::sha256 left = detail::_left(l); FC_ASSERT( secp256k1_ec_pubkey_tweak_add( detail::_get_context(), (unsigned char*) key.begin(), key.size(), (unsigned char*) left.data() ) > 0 ); - extended_public_key result( key, _right(l), i, fingerprint(), depth + 1 ); + extended_public_key result( key, detail::_right(l), i, fingerprint(), depth + 1 ); return result; } - extended_key_data extended_public_key::serialize_extended() const - { - extended_key_data result; - unsigned char* dest = (unsigned char*) result.begin(); - _put( &dest, BTC_EXT_PUB_MAGIC ); - *dest++ = depth; - _put( &dest, parent_fp ); - _put( &dest, child_num ); - memcpy( dest, c.data(), c.data_size() ); dest += 32; - public_key_data key = serialize(); - memcpy( dest, key.begin(), key.size() ); - return result; - } - - fc::string extended_public_key::str() const - { - return _to_base58( serialize_extended() ); - } - - extended_public_key extended_public_key::from_base58( const fc::string& base58 ) - { - unsigned char buffer[78]; - unsigned char* ptr = buffer; - _parse_extended_data( buffer, base58 ); - FC_ASSERT( _get( &ptr ) == BTC_EXT_PUB_MAGIC, "Invalid extended private key" ); - uint8_t d = *ptr++; - int fp = _get( &ptr ); - int cn = _get( &ptr ); - fc::sha256 chain; - memcpy( chain.data(), ptr, chain.data_size() ); ptr += chain.data_size(); - public_key_data key; - memcpy( key.begin(), ptr, key.size() ); - return extended_public_key( key, chain, cn, fp, d ); - } - 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) { } - extended_public_key extended_private_key::get_extended_public_key() const - { - return extended_public_key( get_public_key(), c, child_num, parent_fp, depth ); - } - - extended_private_key extended_private_key::derive_child(int i) const - { - return i < 0 ? derive_hardened_child(i) : derive_normal_child(i); - } - - extended_private_key extended_private_key::derive_normal_child(int i) const - { - const chr37 data = _derive_message( get_public_key().serialize(), i ); - hmac_sha512 mac; - fc::sha512 l = mac.digest( c.data(), c.data_size(), data.begin(), data.size() ); - return private_derive_rest( l, i ); - } - - extended_private_key extended_private_key::derive_hardened_child(int i) const - { - hmac_sha512 mac; - private_key_secret key = get_secret(); - const chr37 data = _derive_message( key, i ); - fc::sha512 l = mac.digest( c.data(), c.data_size(), data.begin(), data.size() ); - return private_derive_rest( l, i ); - } - extended_private_key extended_private_key::private_derive_rest( const fc::sha512& hash, int i) const { - fc::sha256 left = _left(hash); + fc::sha256 left = detail::_left(hash); 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 ), _right(hash), + extended_private_key result( private_key::regenerate( left ), detail::_right(hash), i, fingerprint(), depth + 1 ); return result; } - extended_key_data extended_private_key::serialize_extended() const - { - extended_key_data result; - unsigned char* dest = (unsigned char*) result.begin(); - _put( &dest, BTC_EXT_PRIV_MAGIC ); - *dest++ = depth; - _put( &dest, parent_fp ); - _put( &dest, child_num ); - memcpy( dest, c.data(), c.data_size() ); dest += 32; - *dest++ = 0; - private_key_secret key = get_secret(); - memcpy( dest, key.data(), key.data_size() ); - return result; - } - - fc::string extended_private_key::str() const - { - return _to_base58( serialize_extended() ); - } - - extended_private_key extended_private_key::from_base58( const fc::string& base58 ) - { - unsigned char buffer[78]; - unsigned char* ptr = buffer; - _parse_extended_data( buffer, base58 ); - FC_ASSERT( _get( &ptr ) == BTC_EXT_PRIV_MAGIC, "Invalid extended private key" ); - uint8_t d = *ptr++; - int fp = _get( &ptr ); - int cn = _get( &ptr ); - fc::sha256 chain; - memcpy( chain.data(), ptr, chain.data_size() ); ptr += chain.data_size(); - ptr++; - private_key_secret key; - memcpy( key.data(), ptr, key.data_size() ); - return extended_private_key( private_key::regenerate(key), chain, cn, fp, d ); - } - - extended_private_key extended_private_key::generate_master( const fc::string& seed ) - { - return generate_master( seed.c_str(), seed.size() ); - } - - extended_private_key extended_private_key::generate_master( const char* seed, uint32_t seed_len ) - { - hmac_sha512 mac; - fc::sha512 hash = mac.digest( "Bitcoin seed", 12, seed, seed_len ); - extended_private_key result( private_key::regenerate( _left(hash) ), _right(hash) ); - return result; - } - commitment_type blind( const blind_factor_type& blind, uint64_t value ) { diff --git a/src/crypto/sha224.cpp b/src/crypto/sha224.cpp index d55802cdb..7b87e69e7 100644 --- a/src/crypto/sha224.cpp +++ b/src/crypto/sha224.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -95,4 +96,7 @@ namespace fc { else memset( &bi, char(0), sizeof(bi) ); } + + template<> + unsigned int hmac::internal_block_size() const { return 64; } } diff --git a/src/crypto/sha256.cpp b/src/crypto/sha256.cpp index ae1d6af3c..d715d24a0 100644 --- a/src/crypto/sha256.cpp +++ b/src/crypto/sha256.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -116,4 +117,6 @@ namespace fc { return sha_value._hash[0]; } + template<> + unsigned int hmac::internal_block_size() const { return 64; } } //end namespace fc diff --git a/src/crypto/sha512.cpp b/src/crypto/sha512.cpp index d177fe3e2..c684837da 100644 --- a/src/crypto/sha512.cpp +++ b/src/crypto/sha512.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -101,4 +102,7 @@ namespace fc { else memset( &bi, char(0), sizeof(bi) ); } + + template<> + unsigned int hmac::internal_block_size() const { return 128; } } From 56e98e136a5f2af0a7a07bb734e92cd063f8635f Mon Sep 17 00:00:00 2001 From: Peter Conrad Date: Sat, 11 Jul 2015 21:27:05 +0200 Subject: [PATCH 4/6] Initial blinding implementation, untested --- include/fc/crypto/elliptic.hpp | 36 ++++-- include/fc/crypto/openssl.hpp | 7 +- src/crypto/elliptic_common.cpp | 33 ++++++ src/crypto/elliptic_secp256k1.cpp | 185 ++++++++++++++++++++++++++++++ 4 files changed, 247 insertions(+), 14 deletions(-) diff --git a/include/fc/crypto/elliptic.hpp b/include/fc/crypto/elliptic.hpp index 600d7b346..d31d1c1dc 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 5811390fa..af883d600 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 6de620d4e..f6730356a 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 70882ab62..ee0b7856f 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 ) { From 622de81402d0c76fa6b1d6676bf8be17963b5457 Mon Sep 17 00:00:00 2001 From: Peter Conrad Date: Sun, 12 Jul 2015 23:00:07 +0200 Subject: [PATCH 5/6] Added tests, fixes (sig format, canonicalization) --- include/fc/crypto/elliptic.hpp | 1 + src/crypto/elliptic_secp256k1.cpp | 55 +++++++++++++++++++++++-------- tests/blinding_test.cpp | 27 +++++++++++++++ 3 files changed, 69 insertions(+), 14 deletions(-) diff --git a/include/fc/crypto/elliptic.hpp b/include/fc/crypto/elliptic.hpp index d31d1c1dc..778f2499f 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 ee0b7856f..7a6c2322c 100644 --- a/src/crypto/elliptic_secp256k1.cpp +++ b/src/crypto/elliptic_secp256k1.cpp @@ -178,37 +178,47 @@ 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 from_bignum( const ssl_bignum& in, private_key_secret& out ) + static void to_bignum( const private_key_secret& in, ssl_bignum& out ) { - unsigned int len = BN_num_bytes( in ); - if ( len > out.data_size() ) + 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[len]; + unsigned char buffer[l]; BN_bn2bin( in, buffer ); - memcpy( (unsigned char*) out.data(), buffer + len - out.data_size(), out.data_size() ); + memcpy( out, buffer + l - len, len ); } else { - memset( out.data(), 0, out.data_size() - len ); - BN_bn2bin( in, (unsigned char*) out.data() + out.data_size() - len ); + memset( out, 0, len - l ); + BN_bn2bin( in, out + len - l ); } } + static void from_bignum( const ssl_bignum& in, private_key_secret& out ) + { + from_bignum( in, (unsigned char*) out.data(), out.data_size() ); + } + static void invert( const private_key_secret& in, private_key_secret& out ) { ssl_bignum bn_in; @@ -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 2b641674d..65dc77db2 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() ); + } + } +} From ef92e68146f67afb4179470755d843bbf88b714e Mon Sep 17 00:00:00 2001 From: Peter Conrad Date: Sun, 19 Jul 2015 22:56:00 +0200 Subject: [PATCH 6/6] Created + added test vectors, various fixes - working! --- include/fc/crypto/elliptic.hpp | 4 +- src/crypto/elliptic_common.cpp | 19 +++ src/crypto/elliptic_secp256k1.cpp | 93 +++++++++++---- tests/blinding_test.cpp | 188 ++++++++++++++++++++++++++++-- 4 files changed, 272 insertions(+), 32 deletions(-) diff --git a/include/fc/crypto/elliptic.hpp b/include/fc/crypto/elliptic.hpp index 778f2499f..99cd68a83 100644 --- a/include/fc/crypto/elliptic.hpp +++ b/include/fc/crypto/elliptic.hpp @@ -198,7 +198,9 @@ namespace fc { 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; + compact_signature unblind_signature( const extended_public_key& bob, + const blind_signature& sig, + const fc::sha256& hash, int i ) const; private: extended_private_key private_derive_rest( const fc::sha512& hash, diff --git a/src/crypto/elliptic_common.cpp b/src/crypto/elliptic_common.cpp index f6730356a..fd1d64f42 100644 --- a/src/crypto/elliptic_common.cpp +++ b/src/crypto/elliptic_common.cpp @@ -89,6 +89,25 @@ namespace fc { namespace ecc { static private_key_secret order = _get_curve_order(); return order; } + + static private_key_secret _get_half_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 ) ); + BN_rshift1( order, order ); + 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_half_curve_order() + { + static private_key_secret half_order = _get_half_curve_order(); + return half_order; + } } public_key public_key::from_key_data( const public_key_data &data ) { diff --git a/src/crypto/elliptic_secp256k1.cpp b/src/crypto/elliptic_secp256k1.cpp index 7a6c2322c..d6851ac24 100644 --- a/src/crypto/elliptic_secp256k1.cpp +++ b/src/crypto/elliptic_secp256k1.cpp @@ -51,6 +51,7 @@ namespace fc { namespace ecc { fc::sha256 _right( const fc::sha512& v ); const ec_group& get_curve(); const private_key_secret& get_curve_order(); + const private_key_secret& get_half_curve_order(); } static const public_key_data empty_pub; @@ -255,20 +256,24 @@ namespace fc { namespace ecc { *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 void print(const unsigned char* data) { +// for (int i = 0; i < 32; i++) { +// printf("%02x", *data++); +// } +// } +// +// static void print(private_key_secret key) { +// print((unsigned char*) key.data()); +// } +// +// static void print(public_key_data key) { +// print((unsigned char*) key.begin() + 1); +// } static void canonicalize( unsigned char *int256 ) { - if (!(*int256 & 0x80)) + fc::sha256 biggi( (char*) int256, 32 ); + if ( detail::get_half_curve_order() >= biggi ) { return; // nothing to do } @@ -280,6 +285,18 @@ namespace fc { namespace ecc { from_bignum( bn_k, int256, 32 ); } + 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() ) ); +// printf("K: "); print(P); printf("\n"); + 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 ) @@ -287,11 +304,11 @@ namespace fc { namespace ecc { 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 + // prod == c^-1 * d 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 + // accu == prod * P == c^-1 * d * P ec_point point_accu( EC_POINT_new( detail::get_curve() ) ); to_point( accu, point_accu ); @@ -316,6 +333,7 @@ namespace fc { namespace ecc { 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 +// printf("T: "); print(accu); printf("\n"); return public_key( accu ); } @@ -342,6 +360,12 @@ namespace fc { namespace ecc { 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(); +// printf("a: "); print(a); printf("\n"); +// printf("b: "); print(b); printf("\n"); +// printf("c: "); print(c); printf("\n"); +// printf("d: "); print(d); printf("\n"); +// printf("P: "); print(p); printf("\n"); +// printf("Q: "); print(q); printf("\n"); return compute_t( a, b, c, d, p, q ); } @@ -351,6 +375,8 @@ namespace fc { namespace ecc { 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 ); +// printf("hash: "); print(hash); printf("\n"); +// printf("blinded: "); print(a); printf("\n"); return a; } @@ -358,15 +384,15 @@ namespace fc { namespace ecc { { private_key_secret p_inv = derive_normal_child( 2*i ).get_secret(); invert( p_inv, p_inv ); +// printf("p: "); print(p_inv); printf("\n"); 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 ); + FC_ASSERT( secp256k1_ec_privkey_tweak_mul( detail::_get_context(), (unsigned char*) q.data(), (unsigned char*) p.data() ) > 0 ); +// printf("q: "); print(q); printf("\n"); return q; } @@ -376,29 +402,50 @@ namespace fc { namespace ecc { 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 ); +// printf("blind_sig: "); print(p); printf("\n"); return p; } compact_signature extended_private_key::unblind_signature( const extended_public_key& bob, const blind_signature& sig, + 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(); 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 q = bob.generate_q(i); public_key_data k = compute_k( a, c, p ); + public_key_data t = compute_t( a, b, c, d, p, q ).serialize(); + + 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 ); compact_signature result; - *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; +// printf("unblinded: "); print(result.begin() + 33); printf("\n"); + for ( int i = 0; i < 4; i++ ) + { + unsigned char pubkey[33]; + int pklen = 33; + if ( secp256k1_ecdsa_recover_compact( detail::_get_context(), (unsigned char*) hash.data(), + (unsigned char*) result.begin() + 1, + pubkey, &pklen, 1, i ) ) + { + if ( !memcmp( t.begin(), pubkey, sizeof(pubkey) ) ) + { + *result.begin() = 27 + 4 + i; + return result; +// } else { +// printf("Candidate: "); print( pubkey ); printf("\n"); + } + } + } + FC_ASSERT( 0, "Failed to unblind - use different i" ); } commitment_type blind( const blind_factor_type& blind, uint64_t value ) diff --git a/tests/blinding_test.cpp b/tests/blinding_test.cpp index 65dc77db2..318f16169 100644 --- a/tests/blinding_test.cpp +++ b/tests/blinding_test.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include // See https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#Test_Vectors @@ -36,6 +37,12 @@ static fc::string TEST2_M_0_m1_1_m2_PRIV = "xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2Yvm static fc::string TEST2_M_0_m1_1_m2_2_PUB = "xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt"; static fc::string TEST2_M_0_m1_1_m2_2_PRIV = "xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j"; +static fc::string BLIND_K_X = "08be5c5076f8cbd1283cf98e74bff873032b9bc79a0769962bf3900f33c2df6e"; +static fc::string BLIND_T_X = "80deff382af8a8e4a5f297588e44d5bf858f30a524f74b13efcefeb54f4b3f47"; +static fc::string BLINDED_HASH = "7196e80cdafdfdfb7496323ad24bf47dda8447febd7426e444facc04940c7309"; +static fc::string BLIND_SIG = "40d6a477d849cc860df8ad159481f2ffc5b4dc3131b86a799d7d10460824dd53"; +static fc::string UNBLINDED = "700092a72a05e33509f9b068aa1d7c5336d8b5692b4157da199d7ec1e10fd7c0"; + BOOST_AUTO_TEST_CASE(test_extended_keys_1) { char seed[16]; @@ -105,28 +112,193 @@ BOOST_AUTO_TEST_CASE(test_extended_keys_2) 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) +//static void print(const unsigned char* data, int len) { +// for (int i = 0; i < len; i++) { +// printf("%02x", *data++); +// } +//} + +BOOST_AUTO_TEST_CASE(test_blinding_1) { 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++ ) + for ( int i = 0; i < 30; i++ ) { - alice = alice.derive_child( i ); - bob = bob.derive_child( i | 0x80000000 ); - buffer[6] = '0' + i; + buffer[5] = '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() ); + fc::ecc::compact_signature sig = alice.unblind_signature( bob_pub, blind_sig, hash, i ); + fc::ecc::public_key validate( sig, hash ); +// printf("Validated: "); print((unsigned char*) validate.serialize().begin(), 33); +// printf("\nT: "); print((unsigned char*) t.serialize().begin(), 33); printf("\n"); + BOOST_CHECK( validate.serialize() == t.serialize() ); } catch (const fc::exception& e) { printf( "Test %d: %s\n", i, e.to_string().c_str() ); } + alice = alice.derive_child( i ); + bob = bob.derive_child( i | 0x80000000 ); } } + +BOOST_AUTO_TEST_CASE(test_blinding_2) +{ + char message[7] = "test_0"; + 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" ); + fc::ecc::extended_public_key bob_pub = bob.get_extended_public_key(); + fc::sha256 hash = fc::sha256::hash( message, sizeof(message) ); + + fc::ecc::public_key t = alice.blind_public_key( bob_pub, 0 ); + fc::ecc::public_key_data pub = t.serialize(); + char buffer[32]; + fc::from_hex( BLIND_T_X, buffer, sizeof(buffer) ); + BOOST_CHECK( !memcmp( pub.begin() + 1, buffer, sizeof(buffer) ) ); + + fc::ecc::blinded_hash blinded = alice.blind_hash( hash, 0 ); + fc::from_hex( BLINDED_HASH, buffer, sizeof(buffer) ); + BOOST_CHECK( !memcmp( blinded.data(), buffer, sizeof(buffer) ) ); + + fc::ecc::blind_signature blind_sig = bob.blind_sign( blinded, 0 ); + fc::from_hex( BLIND_SIG, buffer, sizeof(buffer) ); + BOOST_CHECK( !memcmp( blind_sig.data(), buffer, sizeof(buffer) ) ); + + fc::ecc::compact_signature sig = alice.unblind_signature( bob_pub, blind_sig, hash, 0 ); + fc::from_hex( BLIND_K_X, buffer, sizeof(buffer) ); + BOOST_CHECK( !memcmp( sig.begin() + 1, buffer, sizeof(buffer) ) ); + fc::from_hex( UNBLINDED, buffer, sizeof(buffer) ); + BOOST_CHECK( !memcmp( sig.begin() + 33, buffer, sizeof(buffer) ) ); +} + +static void to_bignum(const char* data32, fc::ssl_bignum& out) { + unsigned char dummy[33]; dummy[0] = 0; + memcpy(dummy, data32, 32); + BN_bin2bn((unsigned char*) data32, 32, out); +} + +//static void print(const fc::sha256 hash) { +// print((unsigned char*) hash.data(), hash.data_size()); +//} +// +//static void print(const BIGNUM* bn) { +// unsigned char buffer[64]; +// int len = BN_num_bytes(bn); +// if (len > sizeof(buffer)) { +// printf("BN too long - %d bytes?!", len); +// return; +// } +// BN_bn2bin(bn, buffer); +// print(buffer, len); +//} +// +//static void print(const fc::ec_group& curve, const fc::ec_point& p, fc::bn_ctx& ctx) { +// fc::ssl_bignum x; +// fc::ssl_bignum y; +// EC_POINT_get_affine_coordinates_GFp(curve, p, x, y, ctx); +// printf("("); +// print(x); +// printf(", "); +// print(y); +// printf(")"); +//} + +namespace fc { +SSL_TYPE(ec_key, EC_KEY, EC_KEY_free) +} + +BOOST_AUTO_TEST_CASE(openssl_blinding) +{ + // Needed this "test" for producing data for debugging my libsecp256k1 implementation + + char buffer[7] = "test_0"; + 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" ); + fc::ec_group curve(EC_GROUP_new_by_curve_name(NID_secp256k1)); + fc::bn_ctx ctx(BN_CTX_new()); + fc::ssl_bignum n; + EC_GROUP_get_order(curve, n, ctx); + fc::ssl_bignum n_half; + BN_rshift1(n_half, n); + fc::ssl_bignum zero; BN_zero(zero); + + fc::sha256 hash_ = fc::sha256::hash( buffer, sizeof(buffer) ); + fc::ssl_bignum hash; to_bignum(hash_.data(), hash); + fc::ssl_bignum a; to_bignum(alice.derive_hardened_child(0).get_secret().data(), a); + fc::ssl_bignum b; to_bignum(alice.derive_hardened_child(1).get_secret().data(), b); + fc::ssl_bignum c; to_bignum(alice.derive_hardened_child(2).get_secret().data(), c); + fc::ssl_bignum d; to_bignum(alice.derive_hardened_child(3).get_secret().data(), d); + + fc::ec_point P(EC_POINT_new(curve)); + fc::ecc::public_key_data Pd = bob.get_extended_public_key().derive_child(0).serialize(); + fc::ssl_bignum Px; to_bignum(Pd.begin() + 1, Px); + EC_POINT_set_compressed_coordinates_GFp(curve, P, Px, (*Pd.begin()) & 1, ctx); + + fc::ec_point Q(EC_POINT_new(curve)); + fc::ecc::public_key_data Qd = bob.get_extended_public_key().derive_child(1).serialize(); + fc::ssl_bignum Qx; to_bignum(Qd.begin() + 1, Qx); + EC_POINT_set_compressed_coordinates_GFp(curve, Q, Qx, (*Qd.begin()) & 1, ctx); + + // Alice computes K = (c·a)^-1·P and public key T = (a·Kx)^-1·(b·G + Q + d·c^-1·P). + fc::ec_point K(EC_POINT_new(curve)); + fc::ssl_bignum tmp; + BN_mod_mul(tmp, a, c, n, ctx); + BN_mod_inverse(tmp, tmp, n, ctx); + EC_POINT_mul(curve, K, zero, P, tmp, ctx); + + fc::ec_point T(EC_POINT_new(curve)); + BN_mod_inverse(tmp, c, n, ctx); + BN_mod_mul(tmp, d, tmp, n, ctx); + EC_POINT_mul(curve, T, b, P, tmp, ctx); + EC_POINT_add(curve, T, T, Q, ctx); + fc::ssl_bignum Kx; + fc::ssl_bignum Ky; + EC_POINT_get_affine_coordinates_GFp(curve, K, Kx, Ky, ctx); + BN_mod_mul(tmp, a, Kx, n, ctx); + BN_mod_inverse(tmp, tmp, n, ctx); + EC_POINT_mul(curve, T, zero, T, tmp, ctx); + + fc::ssl_bignum blinded; + BN_mod_mul(blinded, a, hash, n, ctx); + BN_mod_add(blinded, blinded, b, n, ctx); + + fc::ssl_bignum p; to_bignum(bob.derive_normal_child(0).get_secret().data(), p); + fc::ssl_bignum q; to_bignum(bob.derive_normal_child(1).get_secret().data(), q); + BN_mod_inverse(p, p, n, ctx); + BN_mod_mul(q, q, p, n, ctx); + fc::ssl_bignum blind_sig; + BN_mod_mul(blind_sig, p, blinded, n, ctx); + BN_mod_add(blind_sig, blind_sig, q, n, ctx); + + fc::ecdsa_sig sig(ECDSA_SIG_new()); + BN_copy(sig->r, Kx); + BN_mod_mul(sig->s, c, blind_sig, n, ctx); + BN_mod_add(sig->s, sig->s, d, n, ctx); + + if (BN_cmp(sig->s, n_half) > 0) { + BN_sub(sig->s, n, sig->s); + } + + fc::ec_key verify(EC_KEY_new()); + EC_KEY_set_public_key(verify, T); + BOOST_CHECK( ECDSA_do_verify( (unsigned char*) hash_.data(), hash_.data_size(), sig, verify ) ); +// printf("a: "); print(a); +// printf("\nb: "); print(b); +// printf("\nc: "); print(c); +// printf("\nd: "); print(d); +// printf("\nP: "); print(curve, P, ctx); +// printf("\nQ: "); print(curve, Q, ctx); +// printf("\nK: "); print(curve, K, ctx); +// printf("\nT: "); print(curve, T, ctx); +// printf("\np: "); print(p); +// printf("\nq: "); print(q); +// printf("\nhash: "); print(hash_); +// printf("\nblinded: "); print(blinded); +// printf("\nblind_sig: "); print(blind_sig); +// printf("\nunblinded: "); print(sig->s); +// printf("\n"); +}