Merge remote-tracking branch 'github/master'

This commit is contained in:
Eric Frias 2015-05-29 09:20:58 -04:00
commit b899bbd476
37 changed files with 2928 additions and 747 deletions

View file

@ -24,6 +24,7 @@ SET( DEFAULT_LIBRARY_INSTALL_DIR lib/ )
SET( DEFAULT_EXECUTABLE_INSTALL_DIR bin/ )
SET( CMAKE_DEBUG_POSTFIX _debug )
SET( BUILD_SHARED_LIBS NO )
SET( ECC_IMPL openssl CACHE STRING "openssl or secp256k1 or mixed" )
set(platformBitness 32)
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
@ -34,6 +35,19 @@ SET (ORIGINAL_LIB_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES})
SET(BOOST_COMPONENTS)
LIST(APPEND BOOST_COMPONENTS thread date_time system filesystem program_options signals serialization chrono unit_test_framework context locale iostreams)
SET( Boost_USE_STATIC_LIBS ON CACHE STRING "ON or OFF" )
IF( ECC_IMPL STREQUAL openssl )
SET( ECC_REST src/crypto/elliptic_impl_pub.cpp )
ELSE( ECC_IMPL STREQUAL openssl )
SET( ECC_LIB secp256k1 )
IF( ECC_IMPL STREQUAL mixed )
SET( ECC_REST src/crypto/elliptic_impl_priv.cpp src/crypto/elliptic_impl_pub.cpp )
ELSE( ECC_IMPL STREQUAL mixed )
SET( ECC_REST src/crypto/elliptic_impl_priv.cpp )
ENDIF( ECC_IMPL STREQUAL mixed )
ENDIF( ECC_IMPL STREQUAL openssl )
IF( WIN32 )
MESSAGE(STATUS "Configuring fc to build on Win32")
@ -44,7 +58,6 @@ IF( WIN32 )
SET(BOOST_ROOT $ENV{BOOST_ROOT})
# set(Boost_USE_DEBUG_PYTHON ON)
set(Boost_USE_MULTITHREADED ON)
set(Boost_USE_STATIC_LIBS ON)
set(BOOST_ALL_DYN_LINK OFF) # force dynamic linking for all libraries
FIND_PACKAGE(Boost 1.53 REQUIRED COMPONENTS ${BOOST_COMPONENTS})
@ -62,7 +75,6 @@ IF( WIN32 )
ELSE(WIN32)
MESSAGE(STATUS "Configuring fc to build on Unix/Apple")
SET(Boost_USE_STATIC_LIBS ON)
LIST(APPEND BOOST_COMPONENTS coroutine)
FIND_PACKAGE(Boost 1.53 REQUIRED COMPONENTS ${BOOST_COMPONENTS})
@ -77,6 +89,8 @@ ELSE(WIN32)
ENDIF(NOT APPLE)
ENDIF(WIN32)
IF(NOT "$ENV{OPENSSL_ROOT_DIR}" STREQUAL "")
set(OPENSSL_ROOT_DIR $ENV{OPENSSL_ROOT_DIR} )
set(OPENSSL_INCLUDE_DIR ${OPENSSL_ROOT_DIR}/include)
@ -124,6 +138,7 @@ set( fc_sources
src/interprocess/file_mapping.cpp
src/interprocess/mmap_struct.cpp
src/rpc/json_connection.cpp
src/rpc/cli.cpp
src/log/log_message.cpp
src/log/logger.cpp
src/log/appender.cpp
@ -148,7 +163,9 @@ set( fc_sources
src/crypto/sha512.cpp
src/crypto/dh.cpp
src/crypto/blowfish.cpp
src/crypto/elliptic.cpp
src/crypto/elliptic_common.cpp
${ECC_REST}
src/crypto/elliptic_${ECC_IMPL}.cpp
src/crypto/rand.cpp
src/crypto/salsa20.cpp
#src/crypto/scrypt.cpp
@ -196,6 +213,25 @@ add_subdirectory( vendor/udt4 )
setup_library( fc SOURCES ${sources} LIBRARY_TYPE STATIC DONT_INSTALL_LIBRARY )
# begin readline stuff
find_package(Curses)
find_package(Readline)
file(GLOB HEADERS "include/bts/cli/*.hpp")
if (READLINE_FOUND)
target_compile_definitions (fc PRIVATE HAVE_READLINE)
set(readline_libraries ${Readline_LIBRARY})
if (CURSES_FOUND)
list(APPEND readline_libraries ${CURSES_LIBRARY})
endif()
set(readline_includes ${Readline_INCLUDE_DIR})
endif()
if(WIN32)
target_compile_definitions( fc PRIVATE _CRT_NONSTDC_NO_DEPRECATE )
endif(WIN32)
# end readline stuff
IF(WIN32)
target_compile_definitions(fc PUBLIC WIN32 NOMINMAX _WIN32_WINNT=0x0501 _CRT_SECURE_NO_WARNINGS
_SCL_SERCURE_NO_WARNINGS
@ -222,6 +258,7 @@ target_include_directories(fc
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include
${Boost_INCLUDE_DIR}
${OPENSSL_INCLUDE_DIR}
"${readline_includes}"
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
#${CMAKE_CURRENT_SOURCE_DIR}/vendor/scrypt-jane
@ -233,8 +270,21 @@ target_include_directories(fc
${CMAKE_CURRENT_SOURCE_DIR}/vendor/websocketpp
)
#target_link_libraries( fc PUBLIC easylzma_static scrypt udt ${Boost_LIBRARIES} ${OPENSSL_LIBRARIES} ${ZLIB_LIBRARIES} ${PLATFORM_SPECIFIC_LIBS} ${RPCRT4} ${CMAKE_DL_LIBS} ${rt_library})
target_link_libraries( fc PUBLIC easylzma_static udt ${Boost_LIBRARIES} ${OPENSSL_LIBRARIES} ${ZLIB_LIBRARIES} ${PLATFORM_SPECIFIC_LIBS} ${RPCRT4} ${CMAKE_DL_LIBS} ${rt_library})
#target_link_libraries( fc PUBLIC easylzma_static scrypt udt ${Boost_LIBRARIES} ${OPENSSL_LIBRARIES} ${ZLIB_LIBRARIES} ${PLATFORM_SPECIFIC_LIBS} ${RPCRT4} ${CMAKE_DL_LIBS} ${rt_library} ${ECC_LIB} )
target_link_libraries( fc PUBLIC -L/usr/local/lib easylzma_static udt ${Boost_LIBRARIES} ${OPENSSL_LIBRARIES} ${ZLIB_LIBRARIES} ${PLATFORM_SPECIFIC_LIBS} ${RPCRT4} ${CMAKE_DL_LIBS} ${rt_library} ${readline_libraries} ${ECC_LIB} )
if(MSVC)
set_source_files_properties( src/network/http/websocket.cpp PROPERTIES COMPILE_FLAGS "/bigobj" )
endif(MSVC)
IF(NOT Boost_UNIT_TEST_FRAMEWORK_LIBRARY MATCHES "\\.(a|lib)$")
IF(WIN32)
add_definitions(/DBOOST_TEST_DYN_LINK)
ELSE(WIN32)
add_definitions(-DBOOST_TEST_DYN_LINK)
ENDIF(WIN32)
ENDIF()
add_executable( api tests/api.cpp )
target_link_libraries( api fc )
@ -248,6 +298,9 @@ add_executable( task_cancel_test tests/task_cancel.cpp )
target_link_libraries( task_cancel_test fc )
add_executable( bloom_test tests/bloom_test.cpp )
target_link_libraries( bloom_test fc )
add_executable( real128_test tests/real128_test.cpp )
target_link_libraries( real128_test fc )

View file

@ -0,0 +1,47 @@
# - Try to find readline include dirs and libraries
#
# Usage of this module as follows:
#
# find_package(Readline)
#
# Variables used by this module, they can change the default behaviour and need
# to be set before calling find_package:
#
# Readline_ROOT_DIR Set this variable to the root installation of
# readline if the module has problems finding the
# proper installation path.
#
# Variables defined by this module:
#
# READLINE_FOUND System has readline, include and lib dirs found
# Readline_INCLUDE_DIR The readline include directories.
# Readline_LIBRARY The readline library.
find_path(Readline_ROOT_DIR
NAMES include/readline/readline.h
)
find_path(Readline_INCLUDE_DIR
NAMES readline/readline.h
HINTS ${Readline_ROOT_DIR}/include
)
find_library(Readline_LIBRARY
NAMES readline
HINTS ${Readline_ROOT_DIR}/lib
)
if(Readline_INCLUDE_DIR AND Readline_LIBRARY AND Ncurses_LIBRARY)
set(READLINE_FOUND TRUE)
else(Readline_INCLUDE_DIR AND Readline_LIBRARY AND Ncurses_LIBRARY)
FIND_LIBRARY(Readline_LIBRARY NAMES readline)
include(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(Readline DEFAULT_MSG Readline_INCLUDE_DIR Readline_LIBRARY )
MARK_AS_ADVANCED(Readline_INCLUDE_DIR Readline_LIBRARY)
endif(Readline_INCLUDE_DIR AND Readline_LIBRARY AND Ncurses_LIBRARY)
mark_as_advanced(
Readline_ROOT_DIR
Readline_INCLUDE_DIR
Readline_LIBRARY
)

43
README-ecc.md Normal file
View file

@ -0,0 +1,43 @@
ECC Support
===========
include/fc/crypto/elliptic.hpp defines an interface for some cryptographic
wrapper classes handling elliptic curve cryptography.
Three implementations of this interface exist. One is based on OpenSSL, the
others are based on libsecp256k1 (see https://github.com/bitcoin/secp256k1 ).
The implementation to be used is selected at compile time using the
cmake variable "ECC_IMPL". It can take one of three values, openssl or
secp256k1 or mixed .
The default is "openssl". The alternatives can be configured when invoking
cmake, for example
cmake -D ECC_IMPL=secp256k1 .
If secp256k1 or mixed is chosen, the secp256k1 library and its include file
must already be installed in the appropriate library / include directories on
your system.
Testing
-------
Type "make ecc_test" to build the ecc_test executable from tests/ecc_test.cpp
with the currently configured ECC implementation.
ecc_test expects two arguments:
ecc_test <pass> <interop-file>
<pass> is a somewhat arbitrary password used for testing.
<interop-file> is a data file containing intermediate test results.
If the file does not exist, it will be created and intermediate results from
the current ECC backend are written to it.
If the file does exist, intermediate results from the current ECC backend
are compared with the file contents.
For a full round of interoperability testing, you can use the script
tests/ecc-interop.sh .
None of the test runs should produce any output.

621
include/fc/bloom_filter.hpp Normal file
View file

@ -0,0 +1,621 @@
#pragma once
/*
*********************************************************************
* *
* Open Bloom Filter *
* *
* Author: Arash Partow - 2000 *
* URL: http://www.partow.net *
* URL: http://www.partow.net/programming/hashfunctions/index.html *
* *
* Copyright notice: *
* Free use of the Open Bloom Filter Library is permitted under the *
* guidelines and in accordance with the most current version of the *
* Common Public License. *
* http://www.opensource.org/licenses/cpl1.0.php *
* *
*********************************************************************
*/
#include <algorithm>
#include <cmath>
#include <cstddef>
#include <iterator>
#include <limits>
#include <string>
#include <vector>
#include <fc/reflect/reflect.hpp>
namespace fc {
static const std::size_t bits_per_char = 0x08; // 8 bits in 1 char(unsigned)
static const unsigned char bit_mask[bits_per_char] = {
0x01, //00000001
0x02, //00000010
0x04, //00000100
0x08, //00001000
0x10, //00010000
0x20, //00100000
0x40, //01000000
0x80 //10000000
};
class bloom_parameters
{
public:
bloom_parameters()
: minimum_size(1),
maximum_size(std::numeric_limits<unsigned long long int>::max()),
minimum_number_of_hashes(1),
maximum_number_of_hashes(std::numeric_limits<unsigned int>::max()),
projected_element_count(10000),
false_positive_probability(1.0 / projected_element_count),
random_seed(0xA5A5A5A55A5A5A5AULL)
{}
virtual ~bloom_parameters()
{}
inline bool operator!()
{
return (minimum_size > maximum_size) ||
(minimum_number_of_hashes > maximum_number_of_hashes) ||
(minimum_number_of_hashes < 1) ||
(0 == maximum_number_of_hashes) ||
(0 == projected_element_count) ||
(false_positive_probability < 0.0) ||
(std::numeric_limits<double>::infinity() == std::abs(false_positive_probability)) ||
(0 == random_seed) ||
(0xFFFFFFFFFFFFFFFFULL == random_seed);
}
//Allowed min/max size of the bloom filter in bits
unsigned long long int minimum_size;
unsigned long long int maximum_size;
//Allowed min/max number of hash functions
unsigned int minimum_number_of_hashes;
unsigned int maximum_number_of_hashes;
//The approximate number of elements to be inserted
//into the bloom filter, should be within one order
//of magnitude. The default is 10000.
unsigned long long int projected_element_count;
//The approximate false positive probability expected
//from the bloom filter. The default is the reciprocal
//of the projected_element_count.
double false_positive_probability;
unsigned long long int random_seed;
struct optimal_parameters_t
{
optimal_parameters_t()
: number_of_hashes(0),
table_size(0)
{}
unsigned int number_of_hashes;
unsigned long long int table_size;
};
optimal_parameters_t optimal_parameters;
virtual bool compute_optimal_parameters()
{
/*
Note:
The following will attempt to find the number of hash functions
and minimum amount of storage bits required to construct a bloom
filter consistent with the user defined false positive probability
and estimated element insertion count.
*/
if (!(*this))
return false;
double min_m = std::numeric_limits<double>::infinity();
double min_k = 0.0;
double curr_m = 0.0;
double k = 1.0;
while (k < 1000.0)
{
double numerator = (- k * projected_element_count);
double denominator = std::log(1.0 - std::pow(false_positive_probability, 1.0 / k));
curr_m = numerator / denominator;
if (curr_m < min_m)
{
min_m = curr_m;
min_k = k;
}
k += 1.0;
}
optimal_parameters_t& optp = optimal_parameters;
optp.number_of_hashes = static_cast<unsigned int>(min_k);
optp.table_size = static_cast<unsigned long long int>(min_m);
optp.table_size += (((optp.table_size % bits_per_char) != 0) ? (bits_per_char - (optp.table_size % bits_per_char)) : 0);
if (optp.number_of_hashes < minimum_number_of_hashes)
optp.number_of_hashes = minimum_number_of_hashes;
else if (optp.number_of_hashes > maximum_number_of_hashes)
optp.number_of_hashes = maximum_number_of_hashes;
if (optp.table_size < minimum_size)
optp.table_size = minimum_size;
else if (optp.table_size > maximum_size)
optp.table_size = maximum_size;
return true;
}
};
class bloom_filter
{
protected:
typedef unsigned int bloom_type;
typedef unsigned char cell_type;
public:
bloom_filter()
: salt_count_(0),
table_size_(0),
raw_table_size_(0),
projected_element_count_(0),
inserted_element_count_(0),
random_seed_(0),
desired_false_positive_probability_(0.0)
{}
bloom_filter(const bloom_parameters& p)
: projected_element_count_(p.projected_element_count),
inserted_element_count_(0),
random_seed_((p.random_seed * 0xA5A5A5A5) + 1),
desired_false_positive_probability_(p.false_positive_probability)
{
salt_count_ = p.optimal_parameters.number_of_hashes;
table_size_ = p.optimal_parameters.table_size;
generate_unique_salt();
raw_table_size_ = table_size_ / bits_per_char;
bit_table_.resize( static_cast<std::size_t>(raw_table_size_) );
//bit_table_ = new cell_type[static_cast<std::size_t>(raw_table_size_)];
std::fill_n(bit_table_.data(),raw_table_size_,0x00);
}
bloom_filter(const bloom_filter& filter)
{
this->operator=(filter);
}
inline bool operator == (const bloom_filter& f) const
{
if (this != &f)
{
return
(salt_count_ == f.salt_count_) &&
(table_size_ == f.table_size_) &&
(raw_table_size_ == f.raw_table_size_) &&
(projected_element_count_ == f.projected_element_count_) &&
(inserted_element_count_ == f.inserted_element_count_) &&
(random_seed_ == f.random_seed_) &&
(desired_false_positive_probability_ == f.desired_false_positive_probability_) &&
(salt_ == f.salt_) &&
std::equal(f.bit_table_.data(),f.bit_table_.data() + raw_table_size_,bit_table_.data());
}
else
return true;
}
inline bool operator != (const bloom_filter& f) const
{
return !operator==(f);
}
inline bloom_filter& operator = (const bloom_filter& f)
{
if (this != &f)
{
salt_count_ = f.salt_count_;
table_size_ = f.table_size_;
raw_table_size_ = f.raw_table_size_;
projected_element_count_ = f.projected_element_count_;
inserted_element_count_ = f.inserted_element_count_;
random_seed_ = f.random_seed_;
desired_false_positive_probability_ = f.desired_false_positive_probability_;
bit_table_.resize( raw_table_size_ );
std::copy(f.bit_table_.data(),f.bit_table_.data() + raw_table_size_,bit_table_.data());
salt_ = f.salt_;
}
return *this;
}
virtual ~bloom_filter()
{
}
inline bool operator!() const
{
return (0 == table_size_);
}
inline void clear()
{
std::fill_n(bit_table_.data(),raw_table_size_,0x00);
inserted_element_count_ = 0;
}
inline void insert(const unsigned char* key_begin, const std::size_t& length)
{
std::size_t bit_index = 0;
std::size_t bit = 0;
for (std::size_t i = 0; i < salt_.size(); ++i)
{
compute_indices(hash_ap(key_begin,length,salt_[i]),bit_index,bit);
bit_table_[bit_index / bits_per_char] |= bit_mask[bit];
}
++inserted_element_count_;
}
template<typename T>
inline void insert(const T& t)
{
// Note: T must be a C++ POD type.
insert(reinterpret_cast<const unsigned char*>(&t),sizeof(T));
}
inline void insert(const std::string& key)
{
insert(reinterpret_cast<const unsigned char*>(key.c_str()),key.size());
}
inline void insert(const char* data, const std::size_t& length)
{
insert(reinterpret_cast<const unsigned char*>(data),length);
}
template<typename InputIterator>
inline void insert(const InputIterator begin, const InputIterator end)
{
InputIterator itr = begin;
while (end != itr)
{
insert(*(itr++));
}
}
inline virtual bool contains(const unsigned char* key_begin, const std::size_t length) const
{
std::size_t bit_index = 0;
std::size_t bit = 0;
for (std::size_t i = 0; i < salt_.size(); ++i)
{
compute_indices(hash_ap(key_begin,length,salt_[i]),bit_index,bit);
if ((bit_table_[bit_index / bits_per_char] & bit_mask[bit]) != bit_mask[bit])
{
return false;
}
}
return true;
}
template<typename T>
inline bool contains(const T& t) const
{
return contains(reinterpret_cast<const unsigned char*>(&t),static_cast<std::size_t>(sizeof(T)));
}
inline bool contains(const std::string& key) const
{
return contains(reinterpret_cast<const unsigned char*>(key.c_str()),key.size());
}
inline bool contains(const char* data, const std::size_t& length) const
{
return contains(reinterpret_cast<const unsigned char*>(data),length);
}
template<typename InputIterator>
inline InputIterator contains_all(const InputIterator begin, const InputIterator end) const
{
InputIterator itr = begin;
while (end != itr)
{
if (!contains(*itr))
{
return itr;
}
++itr;
}
return end;
}
template<typename InputIterator>
inline InputIterator contains_none(const InputIterator begin, const InputIterator end) const
{
InputIterator itr = begin;
while (end != itr)
{
if (contains(*itr))
{
return itr;
}
++itr;
}
return end;
}
inline virtual unsigned long long int size() const
{
return table_size_;
}
inline std::size_t element_count() const
{
return inserted_element_count_;
}
inline double effective_fpp() const
{
/*
Note:
The effective false positive probability is calculated using the
designated table size and hash function count in conjunction with
the current number of inserted elements - not the user defined
predicated/expected number of inserted elements.
*/
return std::pow(1.0 - std::exp(-1.0 * salt_.size() * inserted_element_count_ / size()), 1.0 * salt_.size());
}
inline bloom_filter& operator &= (const bloom_filter& f)
{
/* intersection */
if (
(salt_count_ == f.salt_count_) &&
(table_size_ == f.table_size_) &&
(random_seed_ == f.random_seed_)
)
{
for (std::size_t i = 0; i < raw_table_size_; ++i)
{
bit_table_[i] &= f.bit_table_[i];
}
}
return *this;
}
inline bloom_filter& operator |= (const bloom_filter& f)
{
/* union */
if (
(salt_count_ == f.salt_count_) &&
(table_size_ == f.table_size_) &&
(random_seed_ == f.random_seed_)
)
{
for (std::size_t i = 0; i < raw_table_size_; ++i)
{
bit_table_[i] |= f.bit_table_[i];
}
}
return *this;
}
inline bloom_filter& operator ^= (const bloom_filter& f)
{
/* difference */
if (
(salt_count_ == f.salt_count_) &&
(table_size_ == f.table_size_) &&
(random_seed_ == f.random_seed_)
)
{
for (std::size_t i = 0; i < raw_table_size_; ++i)
{
bit_table_[i] ^= f.bit_table_[i];
}
}
return *this;
}
inline const cell_type* table() const
{
return bit_table_.data();
}
inline std::size_t hash_count()
{
return salt_.size();
}
protected:
inline virtual void compute_indices(const bloom_type& hash, std::size_t& bit_index, std::size_t& bit) const
{
bit_index = hash % table_size_;
bit = bit_index % bits_per_char;
}
void generate_unique_salt()
{
/*
Note:
A distinct hash function need not be implementation-wise
distinct. In the current implementation "seeding" a common
hash function with different values seems to be adequate.
*/
const unsigned int predef_salt_count = 128;
static const bloom_type predef_salt[predef_salt_count] =
{
0xAAAAAAAA, 0x55555555, 0x33333333, 0xCCCCCCCC,
0x66666666, 0x99999999, 0xB5B5B5B5, 0x4B4B4B4B,
0xAA55AA55, 0x55335533, 0x33CC33CC, 0xCC66CC66,
0x66996699, 0x99B599B5, 0xB54BB54B, 0x4BAA4BAA,
0xAA33AA33, 0x55CC55CC, 0x33663366, 0xCC99CC99,
0x66B566B5, 0x994B994B, 0xB5AAB5AA, 0xAAAAAA33,
0x555555CC, 0x33333366, 0xCCCCCC99, 0x666666B5,
0x9999994B, 0xB5B5B5AA, 0xFFFFFFFF, 0xFFFF0000,
0xB823D5EB, 0xC1191CDF, 0xF623AEB3, 0xDB58499F,
0xC8D42E70, 0xB173F616, 0xA91A5967, 0xDA427D63,
0xB1E8A2EA, 0xF6C0D155, 0x4909FEA3, 0xA68CC6A7,
0xC395E782, 0xA26057EB, 0x0CD5DA28, 0x467C5492,
0xF15E6982, 0x61C6FAD3, 0x9615E352, 0x6E9E355A,
0x689B563E, 0x0C9831A8, 0x6753C18B, 0xA622689B,
0x8CA63C47, 0x42CC2884, 0x8E89919B, 0x6EDBD7D3,
0x15B6796C, 0x1D6FDFE4, 0x63FF9092, 0xE7401432,
0xEFFE9412, 0xAEAEDF79, 0x9F245A31, 0x83C136FC,
0xC3DA4A8C, 0xA5112C8C, 0x5271F491, 0x9A948DAB,
0xCEE59A8D, 0xB5F525AB, 0x59D13217, 0x24E7C331,
0x697C2103, 0x84B0A460, 0x86156DA9, 0xAEF2AC68,
0x23243DA5, 0x3F649643, 0x5FA495A8, 0x67710DF8,
0x9A6C499E, 0xDCFB0227, 0x46A43433, 0x1832B07A,
0xC46AFF3C, 0xB9C8FFF0, 0xC9500467, 0x34431BDF,
0xB652432B, 0xE367F12B, 0x427F4C1B, 0x224C006E,
0x2E7E5A89, 0x96F99AA5, 0x0BEB452A, 0x2FD87C39,
0x74B2E1FB, 0x222EFD24, 0xF357F60C, 0x440FCB1E,
0x8BBE030F, 0x6704DC29, 0x1144D12F, 0x948B1355,
0x6D8FD7E9, 0x1C11A014, 0xADD1592F, 0xFB3C712E,
0xFC77642F, 0xF9C4CE8C, 0x31312FB9, 0x08B0DD79,
0x318FA6E7, 0xC040D23D, 0xC0589AA7, 0x0CA5C075,
0xF874B172, 0x0CF914D5, 0x784D3280, 0x4E8CFEBC,
0xC569F575, 0xCDB2A091, 0x2CC016B4, 0x5C5F4421
};
if (salt_count_ <= predef_salt_count)
{
std::copy(predef_salt,
predef_salt + salt_count_,
std::back_inserter(salt_));
for (unsigned int i = 0; i < salt_.size(); ++i)
{
/*
Note:
This is done to integrate the user defined random seed,
so as to allow for the generation of unique bloom filter
instances.
*/
salt_[i] = salt_[i] * salt_[(i + 3) % salt_.size()] + static_cast<bloom_type>(random_seed_);
}
}
else
{
std::copy(predef_salt,predef_salt + predef_salt_count,std::back_inserter(salt_));
srand(static_cast<unsigned int>(random_seed_));
while (salt_.size() < salt_count_)
{
bloom_type current_salt = static_cast<bloom_type>(rand()) * static_cast<bloom_type>(rand());
if (0 == current_salt) continue;
if (salt_.end() == std::find(salt_.begin(), salt_.end(), current_salt))
{
salt_.push_back(current_salt);
}
}
}
}
inline bloom_type hash_ap(const unsigned char* begin, std::size_t remaining_length, bloom_type hash) const
{
const unsigned char* itr = begin;
unsigned int loop = 0;
while (remaining_length >= 8)
{
const unsigned int& i1 = *(reinterpret_cast<const unsigned int*>(itr)); itr += sizeof(unsigned int);
const unsigned int& i2 = *(reinterpret_cast<const unsigned int*>(itr)); itr += sizeof(unsigned int);
hash ^= (hash << 7) ^ i1 * (hash >> 3) ^
(~((hash << 11) + (i2 ^ (hash >> 5))));
remaining_length -= 8;
}
if (remaining_length)
{
if (remaining_length >= 4)
{
const unsigned int& i = *(reinterpret_cast<const unsigned int*>(itr));
if (loop & 0x01)
hash ^= (hash << 7) ^ i * (hash >> 3);
else
hash ^= (~((hash << 11) + (i ^ (hash >> 5))));
++loop;
remaining_length -= 4;
itr += sizeof(unsigned int);
}
if (remaining_length >= 2)
{
const unsigned short& i = *(reinterpret_cast<const unsigned short*>(itr));
if (loop & 0x01)
hash ^= (hash << 7) ^ i * (hash >> 3);
else
hash ^= (~((hash << 11) + (i ^ (hash >> 5))));
++loop;
remaining_length -= 2;
itr += sizeof(unsigned short);
}
if (remaining_length)
{
hash += ((*itr) ^ (hash * 0xA5A5A5A5)) + loop;
}
}
return hash;
}
public:
std::vector<bloom_type> salt_;
std::vector<unsigned char> bit_table_;
unsigned int salt_count_;
unsigned long long int table_size_;
unsigned long long int raw_table_size_;
unsigned long long int projected_element_count_;
unsigned int inserted_element_count_;
unsigned long long int random_seed_;
double desired_false_positive_probability_;
};
inline bloom_filter operator & (const bloom_filter& a, const bloom_filter& b)
{
bloom_filter result = a;
result &= b;
return result;
}
inline bloom_filter operator | (const bloom_filter& a, const bloom_filter& b)
{
bloom_filter result = a;
result |= b;
return result;
}
inline bloom_filter operator ^ (const bloom_filter& a, const bloom_filter& b)
{
bloom_filter result = a;
result ^= b;
return result;
}
} // namespace fc
FC_REFLECT( fc::bloom_filter, (salt_)(bit_table_)(salt_count_)(table_size_)(raw_table_size_)(projected_element_count_)(inserted_element_count_)(random_seed_)(desired_false_positive_probability_) )
FC_REFLECT( fc::bloom_parameters::optimal_parameters_t, (number_of_hashes)(table_size) )
FC_REFLECT( fc::bloom_parameters, (minimum_size)(maximum_size)(minimum_number_of_hashes)(maximum_number_of_hashes)(projected_element_count)(false_positive_probability)(random_seed)(optimal_parameters) )
/*
Note 1:
If it can be guaranteed that bits_per_char will be of the form 2^n then
the following optimization can be used:
hash_table[bit_index >> n] |= bit_mask[bit_index & (bits_per_char - 1)];
Note 2:
For performance reasons where possible when allocating memory it should
be aligned (aligned_alloc) according to the architecture being used.
*/

View file

@ -1,5 +1,6 @@
#pragma once
#include <fc/crypto/bigint.hpp>
#include <fc/crypto/openssl.hpp>
#include <fc/crypto/sha256.hpp>
#include <fc/crypto/sha512.hpp>
#include <fc/fwd.hpp>
@ -31,7 +32,7 @@ namespace fc {
public_key();
public_key(const public_key& k);
~public_key();
bool verify( const fc::sha256& digest, const signature& sig );
// bool verify( const fc::sha256& digest, const signature& sig );
public_key_data serialize()const;
public_key_point_data serialize_ecc_point()const;
@ -43,7 +44,10 @@ namespace fc {
public_key( const compact_signature& c, const fc::sha256& digest, bool check_canonical = true );
bool valid()const;
public_key mult( const fc::sha256& offset );
/** Computes new pubkey = generator * offset + old pubkey ?! */
// public_key mult( const fc::sha256& offset )const;
/** Computes new pubkey = regenerate(offset).pubkey + old pubkey
* = offset * G + 1 * old pubkey ?! */
public_key add( const fc::sha256& offset )const;
public_key( public_key&& pk );
@ -61,11 +65,14 @@ namespace fc {
/// Allows to convert current public key object into base58 number.
std::string to_base58() const;
static std::string to_base58( const public_key_data &key );
static public_key from_base58( const std::string& b58 );
private:
friend class private_key;
fc::fwd<detail::public_key_impl,8> my;
static public_key from_key_data( const public_key_data& v );
static bool is_canonical( const compact_signature& c );
fc::fwd<detail::public_key_impl,33> my;
};
/**
@ -103,9 +110,9 @@ namespace fc {
*/
fc::sha512 get_shared_secret( const public_key& pub )const;
signature sign( const fc::sha256& digest )const;
// signature sign( const fc::sha256& digest )const;
compact_signature sign_compact( const fc::sha256& digest )const;
bool verify( const fc::sha256& digest, const signature& sig );
// bool verify( const fc::sha256& digest, const signature& sig );
public_key get_public_key()const;
@ -123,7 +130,9 @@ namespace fc {
}
private:
fc::fwd<detail::private_key_impl,8> my;
private_key( EC_KEY* k );
static fc::sha256 get_secret( const EC_KEY * const k );
fc::fwd<detail::private_key_impl,32> my;
};
} // namespace ecc
void to_variant( const ecc::private_key& var, variant& vo );

View file

@ -18,6 +18,7 @@ class ripemd160
explicit operator string()const;
char* data()const;
size_t data_size()const { return 160/8; }
static ripemd160 hash( const fc::sha512& h );
static ripemd160 hash( const fc::sha256& h );

View file

@ -14,6 +14,7 @@ class sha1
operator string()const;
char* data()const;
size_t data_size()const { return 20; }
static sha1 hash( const char* d, uint32_t dlen );
static sha1 hash( const string& );

View file

@ -17,6 +17,7 @@ class sha224
operator string()const;
char* data()const;
size_t data_size()const { return 224 / 8; }
static sha224 hash( const char* d, uint32_t dlen );
static sha224 hash( const string& );

View file

@ -18,6 +18,7 @@ class sha256
operator string()const;
char* data()const;
size_t data_size()const { return 256 / 8; }
static sha256 hash( const char* d, uint32_t dlen );
static sha256 hash( const string& );

View file

@ -15,6 +15,7 @@ class sha512
operator string()const;
char* data()const;
size_t data_size()const { return 512 / 8; }
static sha512 hash( const char* d, uint32_t dlen );
static sha512 hash( const string& );

View file

@ -294,6 +294,14 @@ namespace fc
} // namespace fc
#if __APPLE__
#define LIKELY(x) __builtin_expect((long)!!(x), 1L)
#define UNLIKELY(x) __builtin_expect((long)!!(x), 0L)
#else
#define LIKELY(x) (x)
#define UNLIKELY(x) (x)
#endif
/**
*@brief: Workaround for varying preprocessing behavior between MSVC and gcc
*/
@ -304,7 +312,7 @@ namespace fc
#define FC_ASSERT( TEST, ... ) \
FC_EXPAND_MACRO( \
FC_MULTILINE_MACRO_BEGIN \
if( !(TEST) ) \
if( UNLIKELY(!(TEST)) ) \
FC_THROW_EXCEPTION( fc::assert_exception, #TEST ": " __VA_ARGS__ ); \
FC_MULTILINE_MACRO_END \
)

View file

@ -343,8 +343,8 @@ namespace fc { namespace json_relaxed
template<bool strict>
fc::variant parseNumberOrStr( const fc::string& token )
{
{ try {
//ilog( (token) );
size_t i = 0, n = token.length();
if( n == 0 )
FC_THROW_EXCEPTION( parse_error_exception, "expected: non-empty token, got: empty token" );
@ -426,12 +426,14 @@ namespace fc { namespace json_relaxed
if( i >= n )
return parseInt<10>( token, start );
char c = token[i++];
//idump((c)(std::string()+c));
switch( c )
{
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
break;
case '.':
return fc::variant(token);
if( dot_ok )
{
dot_ok = false;
@ -442,7 +444,9 @@ namespace fc { namespace json_relaxed
return fc::variant( fc::to_double(token.c_str()) );
}
//idump((i));
c = token[i+1];
//idump((c));
switch( c )
{
case '0': case '1': case '2': case '3': case '4':
@ -466,7 +470,7 @@ namespace fc { namespace json_relaxed
FC_THROW_EXCEPTION( parse_error_exception, "expected digit after '.'" );
return fc::variant( token );
default:
FC_THROW_EXCEPTION( parse_error_exception, "illegal character '{c}' in token", ( "c", c ) );
FC_THROW_EXCEPTION( parse_error_exception, "illegal character '{c}' in token", ( "c", c )("i",int(c)) );
}
}
else
@ -554,7 +558,7 @@ namespace fc { namespace json_relaxed
FC_THROW_EXCEPTION( parse_error_exception, "illegal character '{c}' in number", ( "c", c ) );
}
}
}
} FC_CAPTURE_AND_RETHROW( (token) ) }
template<typename T, bool strict>
variant_object objectFromStream( T& in )
@ -641,13 +645,13 @@ namespace fc { namespace json_relaxed
template<typename T, bool strict>
variant numberFromStream( T& in )
{
{ try {
fc::string token = tokenFromStream(in);
variant result = json_relaxed::parseNumberOrStr<strict>( token );
if( strict && !(result.is_int64() || result.is_uint64() || result.is_double()) )
FC_THROW_EXCEPTION( parse_error_exception, "expected: number" );
return result;
}
} FC_CAPTURE_AND_RETHROW() }
template<typename T, bool strict>
variant wordFromStream( T& in )

View file

@ -9,18 +9,22 @@
namespace fc { namespace http {
namespace detail {
class websocket_server_impl;
class websocket_tls_server_impl;
class websocket_client_impl;
class websocket_tls_client_impl;
} // namespace detail;
class websocket_connection
{
public:
virtual ~websocket_connection(){};
virtual ~websocket_connection(){}
virtual void send_message( const std::string& message ) = 0;
virtual void close( int64_t code, const std::string& reason ){};
void on_message( const std::string& message ) { _on_message(message); }
string on_http( const std::string& message ) { return _on_http(message); }
void on_message_handler( const std::function<void(const std::string&)>& h ) { _on_message = h; }
void on_http_handler( const std::function<std::string(const std::string&)>& h ) { _on_http = h; }
void set_session_data( fc::any d ){ _session_data = std::move(d); }
fc::any& get_session_data() { return _session_data; }
@ -29,6 +33,7 @@ namespace fc { namespace http {
private:
fc::any _session_data;
std::function<void(const std::string&)> _on_message;
std::function<string(const std::string&)> _on_http;
};
typedef std::shared_ptr<websocket_connection> websocket_connection_ptr;
@ -50,6 +55,24 @@ namespace fc { namespace http {
std::unique_ptr<detail::websocket_server_impl> my;
};
class websocket_tls_server
{
public:
websocket_tls_server( const std::string& server_pem = std::string(),
const std::string& ssl_password = std::string());
~websocket_tls_server();
void on_connection( const on_connection_handler& handler);
void listen( uint16_t port );
void listen( const fc::ip::endpoint& ep );
void start_accept();
private:
friend class detail::websocket_tls_server_impl;
std::unique_ptr<detail::websocket_tls_server_impl> my;
};
class websocket_client
{
public:
@ -57,8 +80,20 @@ namespace fc { namespace http {
~websocket_client();
websocket_connection_ptr connect( const std::string& uri );
websocket_connection_ptr secure_connect( const std::string& uri );
private:
std::unique_ptr<detail::websocket_client_impl> my;
std::unique_ptr<detail::websocket_tls_client_impl> smy;
};
class websocket_tls_client
{
public:
websocket_tls_client();
~websocket_tls_client();
websocket_connection_ptr connect( const std::string& uri );
private:
std::unique_ptr<detail::websocket_tls_client_impl> my;
};
} }

View file

@ -1,6 +1,10 @@
#pragma once
#include <fc/string.hpp>
#include <fc/optional.hpp>
#include <vector>
#include <map>
#include <fc/container/flat_fwd.hpp>
namespace fc {
class value;
class exception;
@ -31,6 +35,27 @@ namespace fc {
return n.c_str();
}
};
template<typename T> struct get_typename<flat_set<T>>
{
static const char* name() {
static std::string n = std::string("flat_set<") + get_typename<T>::name() + ">";
return n.c_str();
}
};
template<typename T> struct get_typename<optional<T>>
{
static const char* name() {
static std::string n = std::string("optional<") + get_typename<T>::name() + ">";
return n.c_str();
}
};
template<typename K,typename V> struct get_typename<std::map<K,V>>
{
static const char* name() {
static std::string n = std::string("std::map<") + get_typename<K>::name() + ","+get_typename<V>::name()+">";
return n.c_str();
}
};
struct signed_int;
struct unsigned_int;

View file

@ -91,7 +91,7 @@ namespace fc {
return _methods[method_id](args);
}
fc::api_connection& get_connection(){ return *_api_connection; }
fc::api_connection& get_connection(){ auto tmp = _api_connection.lock(); FC_ASSERT( tmp, "connection closed"); return *tmp; }
private:
@ -112,14 +112,14 @@ namespace fc {
template<typename R, typename Signature, typename ... Args>
R call_generic( const std::function<R(std::function<Signature>,Args...)>& f, variants::const_iterator a0, variants::const_iterator e )
{
FC_ASSERT( a0 != e );
detail::callback_functor<Signature> arg0( *this, a0->as<uint64_t>() );
FC_ASSERT( a0 != e, "too few arguments passed to method" );
detail::callback_functor<Signature> arg0( get_connection(), a0->as<uint64_t>() );
return call_generic<R,Args...>( this->bind_first_arg<R,std::function<Signature>,Args...>( f, std::function<Signature>(arg0) ), a0+1, e );
}
template<typename R, typename Signature, typename ... Args>
R call_generic( const std::function<R(const std::function<Signature>&,Args...)>& f, variants::const_iterator a0, variants::const_iterator e )
{
FC_ASSERT( a0 != e );
FC_ASSERT( a0 != e, "too few arguments passed to method" );
detail::callback_functor<Signature> arg0( get_connection(), a0->as<uint64_t>() );
return call_generic<R,Args...>( this->bind_first_arg<R,const std::function<Signature>&,Args...>( f, arg0 ), a0+1, e );
}
@ -127,13 +127,13 @@ namespace fc {
template<typename R, typename Arg0, typename ... Args>
R call_generic( const std::function<R(Arg0,Args...)>& f, variants::const_iterator a0, variants::const_iterator e )
{
FC_ASSERT( a0 != e );
FC_ASSERT( a0 != e, "too few arguments passed to method" );
return call_generic<R,Args...>( this->bind_first_arg<R,Arg0,Args...>( f, a0->as< typename std::decay<Arg0>::type >() ), a0+1, e );
}
struct api_visitor
{
api_visitor( generic_api& a, const std::shared_ptr<fc::api_connection>& s ):api(a),_api_con(s){ }
api_visitor( generic_api& a, const std::weak_ptr<fc::api_connection>& s ):api(a),_api_con(s){ }
template<typename Interface, typename Adaptor, typename ... Args>
std::function<variant(const fc::variants&)> to_generic( const std::function<api<Interface,Adaptor>(Args...)>& f )const;
@ -154,11 +154,11 @@ namespace fc {
}
generic_api& api;
const std::shared_ptr<fc::api_connection>& _api_con;
const std::weak_ptr<fc::api_connection>& _api_con;
};
std::shared_ptr<fc::api_connection> _api_connection;
std::weak_ptr<fc::api_connection> _api_connection;
fc::any _api;
std::map< std::string, uint32_t > _by_name;
std::vector< std::function<variant(const variants&)> > _methods;
@ -325,7 +325,7 @@ namespace fc {
generic_api::generic_api( const Api& a, const std::shared_ptr<fc::api_connection>& c )
:_api_connection(c),_api(a)
{
boost::any_cast<const Api&>(a)->visit( api_visitor( *this, _api_connection ) );
boost::any_cast<const Api&>(a)->visit( api_visitor( *this, c ) );
}
template<typename Interface, typename Adaptor, typename ... Args>
@ -335,8 +335,11 @@ namespace fc {
auto api_con = _api_con;
auto gapi = &api;
return [=]( const variants& args ) {
auto con = api_con.lock();
FC_ASSERT( con, "not connected" );
auto api_result = gapi->call_generic( f, args.begin(), args.end() );
return api_con->register_api( api_result );
return con->register_api( api_result );
};
}
template<typename Interface, typename Adaptor, typename ... Args>
@ -346,9 +349,12 @@ namespace fc {
auto api_con = _api_con;
auto gapi = &api;
return [=]( const variants& args )-> fc::variant {
auto con = api_con.lock();
FC_ASSERT( con, "not connected" );
auto api_result = gapi->call_generic( f, args.begin(), args.end() );
if( api_result )
return api_con->register_api( *api_result );
return con->register_api( *api_result );
return variant();
};
}

View file

@ -3,8 +3,11 @@
#include <fc/io/json.hpp>
#include <fc/io/buffered_iostream.hpp>
#include <fc/io/sstream.hpp>
#include <fc/rpc/api_connection.hpp>
#include <fc/thread/thread.hpp>
#include <iostream>
namespace fc { namespace rpc {
/**
@ -47,16 +50,26 @@ namespace fc { namespace rpc {
{
_result_formatters[method] = formatter;
}
virtual void getline( const fc::string& prompt, fc::string& line );
void set_prompt( const string& prompt ) { _prompt = prompt; }
private:
void run()
{
while( !fc::cin.eof() && !_run_complete.canceled() )
while( !_run_complete.canceled() )
{
try {
std::cout << ">>> ";
std::cout.flush();
std::string line;
fc::getline( fc::cin, line );
try
{
getline( _prompt.c_str(), line );
}
catch ( const fc::eof_exception& e )
{
break;
}
std::cout << line << "\n";
line += char(EOF);
fc::variants args = fc::json::variants_from_string(line);;
@ -79,6 +92,7 @@ namespace fc { namespace rpc {
}
}
}
std::string _prompt = ">>>";
std::map<string,std::function<string(variant,const variants&)> > _result_formatters;
fc::future<void> _run_complete;
};

View file

@ -10,6 +10,10 @@ namespace fc { namespace rpc {
class websocket_api_connection : public api_connection
{
public:
~websocket_api_connection()
{
}
websocket_api_connection( fc::http::websocket_connection& c )
:_connection(c)
{
@ -36,7 +40,8 @@ namespace fc { namespace rpc {
return this->receive_call( 0, method_name, args );
});
_connection.on_message_handler( [&]( const std::string& msg ){ on_message(msg); } );
_connection.on_message_handler( [&]( const std::string& msg ){ on_message(msg,true); } );
_connection.on_http_handler( [&]( const std::string& msg ){ return on_message(msg,false); } );
_connection.closed.connect( [this](){ closed(); } );
}
@ -62,7 +67,7 @@ namespace fc { namespace rpc {
protected:
void on_message( const std::string& message )
std::string on_message( const std::string& message, bool send_message = true )
{
try {
auto var = fc::json::from_string(message);
@ -74,14 +79,21 @@ namespace fc { namespace rpc {
auto result = _rpc_state.local_call( call.method, call.params );
if( call.id )
{
_connection.send_message( fc::json::to_string( response( *call.id, result ) ) );
auto reply = fc::json::to_string( response( *call.id, result ) );
if( send_message )
_connection.send_message( reply );
return reply;
}
}
catch ( const fc::exception& e )
{
if( call.id )
{
_connection.send_message( fc::json::to_string( response( *call.id, error_object{ 1, e.to_detail_string(), fc::variant(e)} ) ) );
auto reply = fc::json::to_string( response( *call.id, error_object{ 1, e.to_detail_string(), fc::variant(e)} ) );
if( send_message )
_connection.send_message( reply );
return reply;
}
}
}
@ -92,7 +104,9 @@ namespace fc { namespace rpc {
}
} catch ( const fc::exception& e ) {
wdump((e.to_detail_string()));
return e.to_detail_string();
}
return string();
}
fc::http::websocket_connection& _connection;
fc::rpc::state _rpc_state;

View file

@ -2,12 +2,17 @@
#include <fc/exception/exception.hpp>
#include <fc/reflect/reflect.hpp>
#include <limits>
namespace fc {
/**
* This type is designed to provide automatic checks for
* integer overflow and default initialization. It will
* throw an exception on overflow conditions.
*
* It can only be used on built-in types. In particular,
* safe<uint128_t> is buggy and should not be used.
*/
template<typename T>
struct safe
@ -17,6 +22,10 @@ namespace fc {
safe(){}
safe( const safe& o ):value(o.value){}
static safe max()
{ return std::numeric_limits<T>::max(); }
static safe min()
{ return std::numeric_limits<T>::min(); }
safe& operator += ( const safe& b )
{
@ -41,6 +50,11 @@ namespace fc {
return safe(-value);
}
safe operator++(int) { safe bak = *this; *this += 1; return bak; }
safe& operator++() { return *this += 1; }
safe operator--(int) { safe bak = *this; *this -= 1; return bak; }
safe& operator--() { return *this -= 1; }
friend safe operator - ( const safe& a, const safe& b )
{
safe tmp(a); tmp -= b; return tmp;
@ -50,26 +64,74 @@ namespace fc {
{
return a.value == b.value;
}
friend bool operator == ( const safe& a, const T& b )
{
return a.value == b;
}
friend bool operator == ( const T& a, const safe& b )
{
return a == b.value;
}
friend bool operator != ( const safe& a, const safe& b )
{
return a.value != b.value;
}
friend bool operator != ( const safe& a, const T& b )
{
return a.value != b;
}
friend bool operator != ( const T& a, const safe& b )
{
return a != b.value;
}
friend bool operator < ( const safe& a, const safe& b )
{
return a.value < b.value;
}
friend bool operator < ( const safe& a, const T& b )
{
return a.value < b;
}
friend bool operator < ( const T& a, const safe& b )
{
return a < b.value;
}
friend bool operator > ( const safe& a, const safe& b )
{
return a.value > b.value;
}
friend bool operator > ( const safe& a, const T& b )
{
return a.value > b;
}
friend bool operator > ( const T& a, const safe& b )
{
return a > b.value;
}
friend bool operator >= ( const safe& a, const safe& b )
{
return a.value >= b.value;
}
friend bool operator >= ( const safe& a, const T& b )
{
return a.value >= b;
}
friend bool operator >= ( const T& a, const safe& b )
{
return a >= b.value;
}
friend bool operator <= ( const safe& a, const safe& b )
{
return a.value <= b.value;
}
friend bool operator <= ( const safe& a, const T& b )
{
return a.value <= b;
}
friend bool operator <= ( const T& a, const safe& b )
{
return a <= b.value;
}
T value = 0;
};

View file

@ -155,6 +155,7 @@ struct type_info<T&, Ts...> {
static const bool no_reference_types = false;
static const bool no_duplicates = position<T, Ts...>::pos == -1 && type_info<Ts...>::no_duplicates;
static const size_t size = type_info<Ts...>::size > sizeof(T&) ? type_info<Ts...>::size : sizeof(T&);
static const size_t count = 1 + type_info<Ts...>::count;
};
template<typename T, typename... Ts>
@ -162,12 +163,14 @@ struct type_info<T, Ts...> {
static const bool no_reference_types = type_info<Ts...>::no_reference_types;
static const bool no_duplicates = position<T, Ts...>::pos == -1 && type_info<Ts...>::no_duplicates;
static const size_t size = type_info<Ts...>::size > sizeof(T) ? type_info<Ts...>::size : sizeof(T&);
static const size_t count = 1 + type_info<Ts...>::count;
};
template<>
struct type_info<> {
static const bool no_reference_types = true;
static const bool no_duplicates = true;
static const size_t count = 0;
static const size_t size = 0;
};
@ -314,7 +317,9 @@ public:
return impl::storage_ops<0, Types...>::apply(_tag, storage, v);
}
static int count() { return impl::type_info<Types...>::count; }
void set_which( int w ) {
FC_ASSERT( w < count() );
this->~static_variant();
_tag = w;
impl::storage_ops<0, Types...>::con(_tag, storage);
@ -369,4 +374,5 @@ struct visitor {
s.visit( to_static_variant(ar[1]) );
}
template<typename... T> struct get_typename<T...> { static const char* name() { return typeid(static_variant<T...>).name(); } };
} // namespace fc

View file

@ -39,6 +39,7 @@ namespace fc
bool operator == ( const uint128& o )const{ return hi == o.hi && lo == o.lo; }
bool operator != ( const uint128& o )const{ return hi != o.hi || lo != o.lo; }
bool operator < ( const uint128& o )const { return (hi == o.hi) ? lo < o.lo : hi < o.hi; }
bool operator < ( const int64_t& o )const { return *this < uint128(o); }
bool operator !()const { return !(hi !=0 || lo != 0); }
uint128 operator -()const { return ++uint128( ~hi, ~lo ); }
uint128 operator ~()const { return uint128( ~hi, ~lo ); }
@ -72,10 +73,15 @@ namespace fc
friend uint128 operator << ( const uint128& l, const uint128& r ) { return uint128(l)<<=r; }
friend uint128 operator >> ( const uint128& l, const uint128& r ) { return uint128(l)>>=r; }
friend bool operator > ( const uint128& l, const uint128& r ) { return r < l; }
friend bool operator > ( const uint128& l, const int64_t& r ) { return uint128(r) < l; }
friend bool operator > ( const int64_t& l, const uint128& r ) { return r < uint128(l); }
friend bool operator >= ( const uint128& l, const uint128& r ) { return l == r || l > r; }
friend bool operator >= ( const uint128& l, const int64_t& r ) { return l >= uint128(r); }
friend bool operator >= ( const int64_t& l, const uint128& r ) { return uint128(l) >= r; }
friend bool operator <= ( const uint128& l, const uint128& r ) { return l == r || l < r; }
friend bool operator <= ( const uint128& l, const int64_t& r ) { return l <= uint128(r); }
friend bool operator <= ( const int64_t& l, const uint128& r ) { return uint128(l) <= r; }
friend std::size_t hash_value( const uint128& v ) { return city_hash_size_t((const char*)&v, sizeof(v)); }
@ -102,10 +108,9 @@ namespace fc
static void full_product( const uint128& a, const uint128& b, uint128& result_hi, uint128& result_lo );
private:
// fields must be public for serialization
uint64_t hi;
uint64_t lo;
};
static_assert( sizeof(uint128) == 2*sizeof(uint64_t), "validate packing assumptions" );
@ -139,6 +144,8 @@ namespace std
};
}
FC_REFLECT( fc::uint128_t, (hi)(lo) )
#ifdef _MSC_VER
#pragma warning (pop)
#endif ///_MSC_VER

View file

@ -0,0 +1,25 @@
#pragma once
#include <fc/crypto/elliptic.hpp>
/* private_key_impl based on libsecp256k1
* used by mixed + secp256k1
*/
namespace fc { namespace ecc { namespace detail {
const secp256k1_context_t* _get_context();
void _init_lib();
class private_key_impl
{
public:
private_key_impl() noexcept;
private_key_impl( const private_key_impl& cpy ) noexcept;
private_key_impl& operator=( const private_key_impl& pk ) noexcept;
private_key_secret _key;
};
}}}

View file

@ -0,0 +1,33 @@
#pragma once
#include <fc/crypto/elliptic.hpp>
#include <boost/config.hpp>
/* public_key_impl implementation based on openssl
* used by mixed + openssl
*/
namespace fc { namespace ecc { namespace detail {
void _init_lib();
class public_key_impl
{
public:
public_key_impl() BOOST_NOEXCEPT;
public_key_impl( const public_key_impl& cpy ) BOOST_NOEXCEPT;
public_key_impl( public_key_impl&& cpy ) BOOST_NOEXCEPT;
~public_key_impl() BOOST_NOEXCEPT;
public_key_impl& operator=( const public_key_impl& pk ) BOOST_NOEXCEPT;
public_key_impl& operator=( public_key_impl&& pk ) BOOST_NOEXCEPT;
static int ECDSA_SIG_recover_key_GFp(EC_KEY *eckey, ECDSA_SIG *ecsig, const unsigned char *msg, int msglen, int recid, int check);
EC_KEY* _key = nullptr;
private:
void free_key() BOOST_NOEXCEPT;
};
}}}

View file

@ -1,668 +0,0 @@
#include <fc/crypto/elliptic.hpp>
#include <fc/crypto/base58.hpp>
#include <fc/crypto/openssl.hpp>
#include <fc/fwd_impl.hpp>
#include <fc/exception/exception.hpp>
#include <fc/log/logger.hpp>
#include <assert.h>
namespace fc { namespace ecc {
namespace detail
{
class public_key_impl
{
public:
public_key_impl()
:_key(nullptr)
{
static int init = init_openssl();
}
~public_key_impl()
{
if( _key != nullptr )
{
EC_KEY_free(_key);
}
}
public_key_impl( const public_key_impl& cpy )
{
_key = cpy._key ? EC_KEY_dup( cpy._key ) : nullptr;
}
EC_KEY* _key;
};
class private_key_impl
{
public:
private_key_impl()
:_key(nullptr)
{
static int init = init_openssl();
}
~private_key_impl()
{
if( _key != nullptr )
{
EC_KEY_free(_key);
}
}
private_key_impl( const private_key_impl& cpy )
{
_key = cpy._key ? EC_KEY_dup( cpy._key ) : nullptr;
}
EC_KEY* _key;
};
}
void * ecies_key_derivation(const void *input, size_t ilen, void *output, size_t *olen)
{
if (*olen < SHA512_DIGEST_LENGTH) {
return NULL;
}
*olen = SHA512_DIGEST_LENGTH;
return (void*)SHA512((const unsigned char*)input, ilen, (unsigned char*)output);
}
// Perform ECDSA key recovery (see SEC1 4.1.6) for curves over (mod p)-fields
// recid selects which key is recovered
// if check is non-zero, additional checks are performed
int ECDSA_SIG_recover_key_GFp(EC_KEY *eckey, ECDSA_SIG *ecsig, const unsigned char *msg, int msglen, int recid, int check)
{
if (!eckey) FC_THROW_EXCEPTION( exception, "null key" );
int ret = 0;
BN_CTX *ctx = NULL;
BIGNUM *x = NULL;
BIGNUM *e = NULL;
BIGNUM *order = NULL;
BIGNUM *sor = NULL;
BIGNUM *eor = NULL;
BIGNUM *field = NULL;
EC_POINT *R = NULL;
EC_POINT *O = NULL;
EC_POINT *Q = NULL;
BIGNUM *rr = NULL;
BIGNUM *zero = NULL;
int n = 0;
int i = recid / 2;
const EC_GROUP *group = EC_KEY_get0_group(eckey);
if ((ctx = BN_CTX_new()) == NULL) { ret = -1; goto err; }
BN_CTX_start(ctx);
order = BN_CTX_get(ctx);
if (!EC_GROUP_get_order(group, order, ctx)) { ret = -2; goto err; }
x = BN_CTX_get(ctx);
if (!BN_copy(x, order)) { ret=-1; goto err; }
if (!BN_mul_word(x, i)) { ret=-1; goto err; }
if (!BN_add(x, x, ecsig->r)) { ret=-1; goto err; }
field = BN_CTX_get(ctx);
if (!EC_GROUP_get_curve_GFp(group, field, NULL, NULL, ctx)) { ret=-2; goto err; }
if (BN_cmp(x, field) >= 0) { ret=0; goto err; }
if ((R = EC_POINT_new(group)) == NULL) { ret = -2; goto err; }
if (!EC_POINT_set_compressed_coordinates_GFp(group, R, x, recid % 2, ctx)) { ret=0; goto err; }
if (check)
{
if ((O = EC_POINT_new(group)) == NULL) { ret = -2; goto err; }
if (!EC_POINT_mul(group, O, NULL, R, order, ctx)) { ret=-2; goto err; }
if (!EC_POINT_is_at_infinity(group, O)) { ret = 0; goto err; }
}
if ((Q = EC_POINT_new(group)) == NULL) { ret = -2; goto err; }
n = EC_GROUP_get_degree(group);
e = BN_CTX_get(ctx);
if (!BN_bin2bn(msg, msglen, e)) { ret=-1; goto err; }
if (8*msglen > n) BN_rshift(e, e, 8-(n & 7));
zero = BN_CTX_get(ctx);
if (!BN_zero(zero)) { ret=-1; goto err; }
if (!BN_mod_sub(e, zero, e, order, ctx)) { ret=-1; goto err; }
rr = BN_CTX_get(ctx);
if (!BN_mod_inverse(rr, ecsig->r, order, ctx)) { ret=-1; goto err; }
sor = BN_CTX_get(ctx);
if (!BN_mod_mul(sor, ecsig->s, rr, order, ctx)) { ret=-1; goto err; }
eor = BN_CTX_get(ctx);
if (!BN_mod_mul(eor, e, rr, order, ctx)) { ret=-1; goto err; }
if (!EC_POINT_mul(group, Q, eor, R, sor, ctx)) { ret=-2; goto err; }
if (!EC_KEY_set_public_key(eckey, Q)) { ret=-2; goto err; }
ret = 1;
err:
if (ctx) {
BN_CTX_end(ctx);
BN_CTX_free(ctx);
}
if (R != NULL) EC_POINT_free(R);
if (O != NULL) EC_POINT_free(O);
if (Q != NULL) EC_POINT_free(Q);
return ret;
}
int static inline EC_KEY_regenerate_key(EC_KEY *eckey, const BIGNUM *priv_key)
{
int ok = 0;
BN_CTX *ctx = NULL;
EC_POINT *pub_key = NULL;
if (!eckey) return 0;
const EC_GROUP *group = EC_KEY_get0_group(eckey);
if ((ctx = BN_CTX_new()) == NULL)
goto err;
pub_key = EC_POINT_new(group);
if (pub_key == NULL)
goto err;
if (!EC_POINT_mul(group, pub_key, priv_key, NULL, NULL, ctx))
goto err;
EC_KEY_set_private_key(eckey,priv_key);
EC_KEY_set_public_key(eckey,pub_key);
ok = 1;
err:
if (pub_key) EC_POINT_free(pub_key);
if (ctx != NULL) BN_CTX_free(ctx);
return(ok);
}
/*
public_key::public_key()
:my( new detail::public_key_impl() )
{
}
public_key::public_key( fc::bigint pub_x, fc::bigint pub_y )
:my( new detail::public_key_impl() )
{
}
public_key::~public_key()
{
}
*/
public_key public_key::mult( const fc::sha256& digest )
{
// get point from this public key
const EC_POINT* master_pub = EC_KEY_get0_public_key( my->_key );
ec_group group(EC_GROUP_new_by_curve_name(NID_secp256k1));
ssl_bignum z;
BN_bin2bn((unsigned char*)&digest, sizeof(digest), z);
// multiply by digest
ssl_bignum one;
BN_one(one);
bn_ctx ctx(BN_CTX_new());
ec_point result(EC_POINT_new(group));
EC_POINT_mul(group, result, z, master_pub, one, ctx);
public_key rtn;
rtn.my->_key = EC_KEY_new_by_curve_name( NID_secp256k1 );
EC_KEY_set_public_key(rtn.my->_key,result);
return rtn;
}
bool public_key::valid()const
{
return my->_key != nullptr;
}
public_key public_key::add( const fc::sha256& digest )const
{
try {
ec_group group(EC_GROUP_new_by_curve_name(NID_secp256k1));
bn_ctx ctx(BN_CTX_new());
fc::bigint digest_bi( (char*)&digest, sizeof(digest) );
ssl_bignum order;
EC_GROUP_get_order(group, order, ctx);
if( digest_bi > fc::bigint(order) )
{
FC_THROW_EXCEPTION( exception, "digest > group order" );
}
public_key digest_key = private_key::regenerate(digest).get_public_key();
const EC_POINT* digest_point = EC_KEY_get0_public_key( digest_key.my->_key );
// get point from this public key
const EC_POINT* master_pub = EC_KEY_get0_public_key( my->_key );
ssl_bignum z;
BN_bin2bn((unsigned char*)&digest, sizeof(digest), z);
// multiply by digest
ssl_bignum one;
BN_one(one);
ec_point result(EC_POINT_new(group));
EC_POINT_add(group, result, digest_point, master_pub, ctx);
if (EC_POINT_is_at_infinity(group, result))
{
FC_THROW_EXCEPTION( exception, "point at infinity" );
}
public_key rtn;
rtn.my->_key = EC_KEY_new_by_curve_name( NID_secp256k1 );
EC_KEY_set_public_key(rtn.my->_key,result);
return rtn;
} FC_RETHROW_EXCEPTIONS( debug, "digest: ${digest}", ("digest",digest) );
}
std::string public_key::to_base58() const
{
public_key_data key = serialize();
uint32_t check = (uint32_t)sha256::hash(key.data, sizeof(key))._hash[0];
assert(key.size() + sizeof(check) == 37);
array<char, 37> data;
memcpy(data.data, key.begin(), key.size());
memcpy(data.begin() + key.size(), (const char*)&check, sizeof(check));
return fc::to_base58(data.begin(), data.size());
}
public_key public_key::from_base58( const std::string& b58 )
{
array<char, 37> data;
size_t s = fc::from_base58(b58, (char*)&data, sizeof(data) );
FC_ASSERT( s == sizeof(data) );
public_key_data key;
uint32_t check = (uint32_t)sha256::hash(data.data, sizeof(key))._hash[0];
FC_ASSERT( memcmp( (char*)&check, data.data + sizeof(key), sizeof(check) ) == 0 );
memcpy( (char*)key.data, data.data, sizeof(key) );
return public_key(key);
}
private_key::private_key()
{}
private_key private_key::generate_from_seed( const fc::sha256& seed, const fc::sha256& offset )
{
ssl_bignum z;
BN_bin2bn((unsigned char*)&offset, sizeof(offset), z);
ec_group group(EC_GROUP_new_by_curve_name(NID_secp256k1));
bn_ctx ctx(BN_CTX_new());
ssl_bignum order;
EC_GROUP_get_order(group, order, ctx);
// secexp = (seed + z) % order
ssl_bignum secexp;
BN_bin2bn((unsigned char*)&seed, sizeof(seed), secexp);
BN_add(secexp, secexp, z);
BN_mod(secexp, secexp, order, ctx);
fc::sha256 secret;
assert(BN_num_bytes(secexp) <= int64_t(sizeof(secret)));
auto shift = sizeof(secret) - BN_num_bytes(secexp);
BN_bn2bin(secexp, ((unsigned char*)&secret)+shift);
return regenerate( secret );
}
private_key private_key::regenerate( const fc::sha256& secret )
{
private_key self;
self.my->_key = EC_KEY_new_by_curve_name( NID_secp256k1 );
if( !self.my->_key ) FC_THROW_EXCEPTION( exception, "Unable to generate EC key" );
ssl_bignum bn;
BN_bin2bn( (const unsigned char*)&secret, 32, bn );
if( !EC_KEY_regenerate_key(self.my->_key,bn) )
{
FC_THROW_EXCEPTION( exception, "unable to regenerate key" );
}
return self;
}
fc::sha256 private_key::get_secret()const
{
if( !my->_key )
{
return fc::sha256();
}
fc::sha256 sec;
const BIGNUM* bn = EC_KEY_get0_private_key(my->_key);
if( bn == NULL )
{
FC_THROW_EXCEPTION( exception, "get private key failed" );
}
int nbytes = BN_num_bytes(bn);
BN_bn2bin(bn, &((unsigned char*)&sec)[32-nbytes] );
return sec;
}
private_key private_key::generate()
{
private_key self;
EC_KEY* k = EC_KEY_new_by_curve_name( NID_secp256k1 );
if( !k ) FC_THROW_EXCEPTION( exception, "Unable to generate EC key" );
self.my->_key = k;
if( !EC_KEY_generate_key( self.my->_key ) )
{
FC_THROW_EXCEPTION( exception, "ecc key generation error" );
}
#if 0
= bigint( EC_KEY_get0_private_key( k );
EC_POINT* pub = EC_KEY_get0_public_key( k );
EC_GROUP* group = EC_KEY_get0_group( k );
EC_POINT_get_affine_coordinates_GFp( group, pub, self.my->_pub_x.get(), self.my->_pub_y.get(), nullptr/*ctx*/ );
EC_KEY_free(k);
#endif
return self;
}
signature private_key::sign( const fc::sha256& digest )const
{
unsigned int buf_len = ECDSA_size(my->_key);
// fprintf( stderr, "%d %d\n", buf_len, sizeof(sha256) );
signature sig;
assert( buf_len == sizeof(sig) );
if( !ECDSA_sign( 0,
(const unsigned char*)&digest, sizeof(digest),
(unsigned char*)&sig, &buf_len, my->_key ) )
{
FC_THROW_EXCEPTION( exception, "signing error" );
}
return sig;
}
bool public_key::verify( const fc::sha256& digest, const fc::ecc::signature& sig )
{
return 1 == ECDSA_verify( 0, (unsigned char*)&digest, sizeof(digest), (unsigned char*)&sig, sizeof(sig), my->_key );
}
public_key_data public_key::serialize()const
{
public_key_data dat;
if( !my->_key ) return dat;
EC_KEY_set_conv_form( my->_key, POINT_CONVERSION_COMPRESSED );
/*size_t nbytes = i2o_ECPublicKey( my->_key, nullptr ); */
/*assert( nbytes == 33 )*/
char* front = &dat.data[0];
i2o_ECPublicKey( my->_key, (unsigned char**)&front );
return dat;
/*
EC_POINT* pub = EC_KEY_get0_public_key( my->_key );
EC_GROUP* group = EC_KEY_get0_group( my->_key );
EC_POINT_get_affine_coordinates_GFp( group, pub, self.my->_pub_x.get(), self.my->_pub_y.get(), nullptr );
*/
}
public_key_point_data public_key::serialize_ecc_point()const
{
public_key_point_data dat;
if( !my->_key ) return dat;
EC_KEY_set_conv_form( my->_key, POINT_CONVERSION_UNCOMPRESSED );
char* front = &dat.data[0];
i2o_ECPublicKey( my->_key, (unsigned char**)&front );
return dat;
}
public_key::public_key()
{
}
public_key::~public_key()
{
}
public_key::public_key( const public_key_point_data& dat )
{
const char* front = &dat.data[0];
if( *front == 0 ){}
else
{
/*my->_key = EC_KEY_new_by_curve_name( NID_secp256k1 ); */
my->_key = o2i_ECPublicKey( &my->_key, (const unsigned char**)&front, sizeof(dat) );
if( !my->_key )
{
FC_THROW_EXCEPTION( exception, "error decoding public key", ("s", ERR_error_string( ERR_get_error(), nullptr) ) );
}
}
}
public_key::public_key( const public_key_data& dat )
{
const char* front = &dat.data[0];
if( *front == 0 ){}
else
{
my->_key = EC_KEY_new_by_curve_name( NID_secp256k1 );
my->_key = o2i_ECPublicKey( &my->_key, (const unsigned char**)&front, sizeof(public_key_data) );
if( !my->_key )
{
FC_THROW_EXCEPTION( exception, "error decoding public key", ("s", ERR_error_string( ERR_get_error(), nullptr) ) );
}
}
}
bool private_key::verify( const fc::sha256& digest, const fc::ecc::signature& sig )
{
return 1 == ECDSA_verify( 0, (unsigned char*)&digest, sizeof(digest), (unsigned char*)&sig, sizeof(sig), my->_key );
}
public_key private_key::get_public_key()const
{
public_key pub;
pub.my->_key = EC_KEY_new_by_curve_name( NID_secp256k1 );
EC_KEY_set_public_key( pub.my->_key, EC_KEY_get0_public_key( my->_key ) );
return pub;
}
fc::sha512 private_key::get_shared_secret( const public_key& other )const
{
FC_ASSERT( my->_key != nullptr );
FC_ASSERT( other.my->_key != nullptr );
fc::sha512 buf;
ECDH_compute_key( (unsigned char*)&buf, sizeof(buf), EC_KEY_get0_public_key(other.my->_key), my->_key, ecies_key_derivation );
return buf;
}
private_key::~private_key()
{
}
public_key::public_key( const compact_signature& c, const fc::sha256& digest, bool check_canonical )
{
int nV = c.data[0];
if (nV<27 || nV>=35)
FC_THROW_EXCEPTION( exception, "unable to reconstruct public key from signature" );
ECDSA_SIG *sig = ECDSA_SIG_new();
BN_bin2bn(&c.data[1],32,sig->r);
BN_bin2bn(&c.data[33],32,sig->s);
if( check_canonical )
{
FC_ASSERT( !(c.data[1] & 0x80), "signature is not canonical" );
FC_ASSERT( !(c.data[1] == 0 && !(c.data[2] & 0x80)), "signature is not canonical" );
FC_ASSERT( !(c.data[33] & 0x80), "signature is not canonical" );
FC_ASSERT( !(c.data[33] == 0 && !(c.data[34] & 0x80)), "signature is not canonical" );
}
my->_key = EC_KEY_new_by_curve_name(NID_secp256k1);
if (nV >= 31)
{
EC_KEY_set_conv_form( my->_key, POINT_CONVERSION_COMPRESSED );
nV -= 4;
// fprintf( stderr, "compressed\n" );
}
if (ECDSA_SIG_recover_key_GFp(my->_key, sig, (unsigned char*)&digest, sizeof(digest), nV - 27, 0) == 1)
{
ECDSA_SIG_free(sig);
return;
}
ECDSA_SIG_free(sig);
FC_THROW_EXCEPTION( exception, "unable to reconstruct public key from signature" );
}
compact_signature private_key::sign_compact( const fc::sha256& digest )const
{
try {
FC_ASSERT( my->_key != nullptr );
auto my_pub_key = get_public_key().serialize(); // just for good measure
//ECDSA_SIG *sig = ECDSA_do_sign((unsigned char*)&digest, sizeof(digest), my->_key);
while( true )
{
ecdsa_sig sig = ECDSA_do_sign((unsigned char*)&digest, sizeof(digest), my->_key);
if (sig==nullptr)
FC_THROW_EXCEPTION( exception, "Unable to sign" );
compact_signature csig;
// memset( csig.data, 0, sizeof(csig) );
int nBitsR = BN_num_bits(sig->r);
int nBitsS = BN_num_bits(sig->s);
if (nBitsR <= 256 && nBitsS <= 256)
{
int nRecId = -1;
for (int i=0; i<4; i++)
{
public_key keyRec;
keyRec.my->_key = EC_KEY_new_by_curve_name( NID_secp256k1 );
if (ECDSA_SIG_recover_key_GFp(keyRec.my->_key, sig, (unsigned char*)&digest, sizeof(digest), i, 1) == 1)
{
if (keyRec.serialize() == my_pub_key )
{
nRecId = i;
break;
}
}
}
if (nRecId == -1)
{
FC_THROW_EXCEPTION( exception, "unable to construct recoverable key");
}
unsigned char* result = nullptr;
auto bytes = i2d_ECDSA_SIG( sig, &result );
auto lenR = result[3];
auto lenS = result[5+lenR];
//idump( (result[0])(result[1])(result[2])(result[3])(result[3+lenR])(result[4+lenR])(bytes)(lenR)(lenS) );
if( lenR != 32 ) { free(result); continue; }
if( lenS != 32 ) { free(result); continue; }
//idump( (33-(nBitsR+7)/8) );
//idump( (65-(nBitsS+7)/8) );
//idump( (sizeof(csig) ) );
memcpy( &csig.data[1], &result[4], lenR );
memcpy( &csig.data[33], &result[6+lenR], lenS );
//idump( (csig.data[33]) );
//idump( (csig.data[1]) );
free(result);
//idump( (nRecId) );
csig.data[0] = nRecId+27+4;//(fCompressedPubKey ? 4 : 0);
/*
idump( (csig) );
auto rlen = BN_bn2bin(sig->r,&csig.data[33-(nBitsR+7)/8]);
auto slen = BN_bn2bin(sig->s,&csig.data[65-(nBitsS+7)/8]);
idump( (rlen)(slen) );
*/
}
return csig;
} // while true
} FC_RETHROW_EXCEPTIONS( warn, "sign ${digest}", ("digest", digest)("private_key",*this) );
}
private_key& private_key::operator=( private_key&& pk )
{
if( my->_key )
{
EC_KEY_free(my->_key);
}
my->_key = pk.my->_key;
pk.my->_key = nullptr;
return *this;
}
public_key::public_key( const public_key& pk )
:my(pk.my)
{
}
public_key::public_key( public_key&& pk )
:my( fc::move( pk.my) )
{
}
private_key::private_key( const private_key& pk )
:my(pk.my)
{
}
private_key::private_key( private_key&& pk )
:my( fc::move( pk.my) )
{
}
public_key& public_key::operator=( public_key&& pk )
{
if( my->_key )
{
EC_KEY_free(my->_key);
}
my->_key = pk.my->_key;
pk.my->_key = nullptr;
return *this;
}
public_key& public_key::operator=( const public_key& pk )
{
if( my->_key )
{
EC_KEY_free(my->_key);
}
my->_key = EC_KEY_dup(pk.my->_key);
return *this;
}
private_key& private_key::operator=( const private_key& pk )
{
if( my->_key )
{
EC_KEY_free(my->_key);
}
my->_key = EC_KEY_dup(pk.my->_key);
return *this;
}
}
void to_variant( const ecc::private_key& var, variant& vo )
{
vo = var.get_secret();
}
void from_variant( const variant& var, ecc::private_key& vo )
{
fc::sha256 sec;
from_variant( var, sec );
vo = ecc::private_key::regenerate(sec);
}
void to_variant( const ecc::public_key& var, variant& vo )
{
vo = var.serialize();
}
void from_variant( const variant& var, ecc::public_key& vo )
{
ecc::public_key_data dat;
from_variant( var, dat );
vo = ecc::public_key(dat);
}
}

View file

@ -0,0 +1,122 @@
#include <fc/crypto/base58.hpp>
#include <fc/crypto/elliptic.hpp>
/* stuff common to all ecc implementations */
namespace fc { namespace ecc {
public_key public_key::from_key_data( const public_key_data &data ) {
return public_key(data);
}
std::string public_key::to_base58( const public_key_data &key )
{
uint32_t check = (uint32_t)sha256::hash(key.data, sizeof(key))._hash[0];
assert(key.size() + sizeof(check) == 37);
array<char, 37> data;
memcpy(data.data, key.begin(), key.size());
memcpy(data.begin() + key.size(), (const char*)&check, sizeof(check));
return fc::to_base58(data.begin(), data.size());
}
public_key public_key::from_base58( const std::string& b58 )
{
array<char, 37> data;
size_t s = fc::from_base58(b58, (char*)&data, sizeof(data) );
FC_ASSERT( s == sizeof(data) );
public_key_data key;
uint32_t check = (uint32_t)sha256::hash(data.data, sizeof(key))._hash[0];
FC_ASSERT( memcmp( (char*)&check, data.data + sizeof(key), sizeof(check) ) == 0 );
memcpy( (char*)key.data, data.data, sizeof(key) );
return from_key_data(key);
}
bool public_key::is_canonical( const compact_signature& c ) {
return !(c.data[1] & 0x80)
&& !(c.data[1] == 0 && !(c.data[2] & 0x80))
&& !(c.data[33] & 0x80)
&& !(c.data[33] == 0 && !(c.data[34] & 0x80));
}
private_key private_key::generate_from_seed( const fc::sha256& seed, const fc::sha256& offset )
{
ssl_bignum z;
BN_bin2bn((unsigned char*)&offset, sizeof(offset), z);
ec_group group(EC_GROUP_new_by_curve_name(NID_secp256k1));
bn_ctx ctx(BN_CTX_new());
ssl_bignum order;
EC_GROUP_get_order(group, order, ctx);
// secexp = (seed + z) % order
ssl_bignum secexp;
BN_bin2bn((unsigned char*)&seed, sizeof(seed), secexp);
BN_add(secexp, secexp, z);
BN_mod(secexp, secexp, order, ctx);
fc::sha256 secret;
assert(BN_num_bytes(secexp) <= int64_t(sizeof(secret)));
auto shift = sizeof(secret) - BN_num_bytes(secexp);
BN_bn2bin(secexp, ((unsigned char*)&secret)+shift);
return regenerate( secret );
}
fc::sha256 private_key::get_secret( const EC_KEY * const k )
{
if( !k )
{
return fc::sha256();
}
fc::sha256 sec;
const BIGNUM* bn = EC_KEY_get0_private_key(k);
if( bn == NULL )
{
FC_THROW_EXCEPTION( exception, "get private key failed" );
}
int nbytes = BN_num_bytes(bn);
BN_bn2bin(bn, &((unsigned char*)&sec)[32-nbytes] );
return sec;
}
private_key private_key::generate()
{
EC_KEY* k = EC_KEY_new_by_curve_name( NID_secp256k1 );
if( !k ) FC_THROW_EXCEPTION( exception, "Unable to generate EC key" );
if( !EC_KEY_generate_key( k ) )
{
FC_THROW_EXCEPTION( exception, "ecc key generation error" );
}
return private_key( k );
}
}
void to_variant( const ecc::private_key& var, variant& vo )
{
vo = var.get_secret();
}
void from_variant( const variant& var, ecc::private_key& vo )
{
fc::sha256 sec;
from_variant( var, sec );
vo = ecc::private_key::regenerate(sec);
}
void to_variant( const ecc::public_key& var, variant& vo )
{
vo = var.serialize();
}
void from_variant( const variant& var, ecc::public_key& vo )
{
ecc::public_key_data dat;
from_variant( var, dat );
vo = ecc::public_key(dat);
}
}

View file

@ -0,0 +1,102 @@
#include <fc/fwd_impl.hpp>
#include <secp256k1.h>
#include "_elliptic_impl_priv.hpp"
/* used by mixed + secp256k1 */
namespace fc { namespace ecc {
namespace detail {
private_key_impl::private_key_impl() noexcept
{
_init_lib();
}
private_key_impl::private_key_impl( const private_key_impl& cpy ) noexcept
{
_init_lib();
this->_key = cpy._key;
}
private_key_impl& private_key_impl::operator=( const private_key_impl& pk ) noexcept
{
_key = pk._key;
return *this;
}
}
static const private_key_secret empty_priv;
private_key::private_key() {}
private_key::private_key( const private_key& pk ) : my( pk.my ) {}
private_key::private_key( private_key&& pk ) : my( std::move( pk.my ) ) {}
private_key::~private_key() {}
private_key& private_key::operator=( private_key&& pk )
{
my = std::move( pk.my );
return *this;
}
private_key& private_key::operator=( const private_key& pk )
{
my = pk.my;
return *this;
}
private_key private_key::regenerate( const fc::sha256& secret )
{
private_key self;
self.my->_key = secret;
return self;
}
fc::sha256 private_key::get_secret()const
{
return my->_key;
}
private_key::private_key( EC_KEY* k )
{
my->_key = get_secret( k );
EC_KEY_free(k);
}
public_key private_key::get_public_key()const
{
FC_ASSERT( my->_key != empty_priv );
public_key_data pub;
unsigned int pk_len;
FC_ASSERT( secp256k1_ec_pubkey_create( detail::_get_context(), (unsigned char*) pub.begin(), (int*) &pk_len, (unsigned char*) my->_key.data(), 1 ) );
FC_ASSERT( pk_len == pub.size() );
return public_key(pub);
}
static int extended_nonce_function( unsigned char *nonce32, const unsigned char *msg32,
const unsigned char *key32, unsigned int attempt,
const void *data ) {
unsigned int* extra = (unsigned int*) data;
(*extra)++;
return secp256k1_nonce_function_default( nonce32, msg32, key32, *extra, nullptr );
}
compact_signature private_key::sign_compact( const fc::sha256& digest )const
{
FC_ASSERT( my->_key != empty_priv );
compact_signature result;
int recid;
unsigned int counter = 0;
do
{
FC_ASSERT( secp256k1_ecdsa_sign_compact( detail::_get_context(), (unsigned char*) digest.data(), (unsigned char*) result.begin() + 1, (unsigned char*) my->_key.data(), extended_nonce_function, &counter, &recid ));
} while( !public_key::is_canonical( result ) );
result.begin()[0] = 27 + 4 + recid;
return result;
}
}}

View file

@ -0,0 +1,358 @@
#include <fc/fwd_impl.hpp>
#include <boost/config.hpp>
#include "_elliptic_impl_pub.hpp"
/* used by mixed + openssl */
namespace fc { namespace ecc {
namespace detail {
public_key_impl::public_key_impl() BOOST_NOEXCEPT
{
_init_lib();
}
public_key_impl::public_key_impl( const public_key_impl& cpy ) BOOST_NOEXCEPT
{
_init_lib();
*this = cpy;
}
public_key_impl::public_key_impl( public_key_impl&& cpy ) BOOST_NOEXCEPT
{
_init_lib();
*this = cpy;
}
public_key_impl::~public_key_impl() BOOST_NOEXCEPT
{
free_key();
}
public_key_impl& public_key_impl::operator=( const public_key_impl& pk ) BOOST_NOEXCEPT
{
if (pk._key == nullptr)
{
free_key();
} else if ( _key == nullptr ) {
_key = EC_KEY_dup( pk._key );
} else {
EC_KEY_copy( _key, pk._key );
}
return *this;
}
public_key_impl& public_key_impl::operator=( public_key_impl&& pk ) BOOST_NOEXCEPT
{
if ( this != &pk ) {
free_key();
_key = pk._key;
pk._key = nullptr;
}
return *this;
}
void public_key_impl::free_key() BOOST_NOEXCEPT
{
if( _key != nullptr )
{
EC_KEY_free(_key);
_key = nullptr;
}
}
// Perform ECDSA key recovery (see SEC1 4.1.6) for curves over (mod p)-fields
// recid selects which key is recovered
// if check is non-zero, additional checks are performed
int public_key_impl::ECDSA_SIG_recover_key_GFp(EC_KEY *eckey, ECDSA_SIG *ecsig,
const unsigned char *msg,
int msglen, int recid, int check)
{
if (!eckey) FC_THROW_EXCEPTION( exception, "null key" );
int ret = 0;
BN_CTX *ctx = NULL;
BIGNUM *x = NULL;
BIGNUM *e = NULL;
BIGNUM *order = NULL;
BIGNUM *sor = NULL;
BIGNUM *eor = NULL;
BIGNUM *field = NULL;
EC_POINT *R = NULL;
EC_POINT *O = NULL;
EC_POINT *Q = NULL;
BIGNUM *rr = NULL;
BIGNUM *zero = NULL;
int n = 0;
int i = recid / 2;
const EC_GROUP *group = EC_KEY_get0_group(eckey);
if ((ctx = BN_CTX_new()) == NULL) { ret = -1; goto err; }
BN_CTX_start(ctx);
order = BN_CTX_get(ctx);
if (!EC_GROUP_get_order(group, order, ctx)) { ret = -2; goto err; }
x = BN_CTX_get(ctx);
if (!BN_copy(x, order)) { ret=-1; goto err; }
if (!BN_mul_word(x, i)) { ret=-1; goto err; }
if (!BN_add(x, x, ecsig->r)) { ret=-1; goto err; }
field = BN_CTX_get(ctx);
if (!EC_GROUP_get_curve_GFp(group, field, NULL, NULL, ctx)) { ret=-2; goto err; }
if (BN_cmp(x, field) >= 0) { ret=0; goto err; }
if ((R = EC_POINT_new(group)) == NULL) { ret = -2; goto err; }
if (!EC_POINT_set_compressed_coordinates_GFp(group, R, x, recid % 2, ctx)) { ret=0; goto err; }
if (check)
{
if ((O = EC_POINT_new(group)) == NULL) { ret = -2; goto err; }
if (!EC_POINT_mul(group, O, NULL, R, order, ctx)) { ret=-2; goto err; }
if (!EC_POINT_is_at_infinity(group, O)) { ret = 0; goto err; }
}
if ((Q = EC_POINT_new(group)) == NULL) { ret = -2; goto err; }
n = EC_GROUP_get_degree(group);
e = BN_CTX_get(ctx);
if (!BN_bin2bn(msg, msglen, e)) { ret=-1; goto err; }
if (8*msglen > n) BN_rshift(e, e, 8-(n & 7));
zero = BN_CTX_get(ctx);
if (!BN_zero(zero)) { ret=-1; goto err; }
if (!BN_mod_sub(e, zero, e, order, ctx)) { ret=-1; goto err; }
rr = BN_CTX_get(ctx);
if (!BN_mod_inverse(rr, ecsig->r, order, ctx)) { ret=-1; goto err; }
sor = BN_CTX_get(ctx);
if (!BN_mod_mul(sor, ecsig->s, rr, order, ctx)) { ret=-1; goto err; }
eor = BN_CTX_get(ctx);
if (!BN_mod_mul(eor, e, rr, order, ctx)) { ret=-1; goto err; }
if (!EC_POINT_mul(group, Q, eor, R, sor, ctx)) { ret=-2; goto err; }
if (!EC_KEY_set_public_key(eckey, Q)) { ret=-2; goto err; }
ret = 1;
err:
if (ctx) {
BN_CTX_end(ctx);
BN_CTX_free(ctx);
}
if (R != NULL) EC_POINT_free(R);
if (O != NULL) EC_POINT_free(O);
if (Q != NULL) EC_POINT_free(Q);
return ret;
}
}
public_key::public_key() {}
public_key::public_key( const public_key& pk ) : my( pk.my ) {}
public_key::public_key( public_key&& pk ) : my( std::move( pk.my ) ) {}
public_key::~public_key() {}
public_key& public_key::operator=( public_key&& pk )
{
my = std::move(pk.my);
return *this;
}
public_key& public_key::operator=( const public_key& pk )
{
my = pk.my;
return *this;
}
bool public_key::valid()const
{
return my->_key != nullptr;
}
/* WARNING! This implementation is broken, it is actually equivalent to
* public_key::add()!
*/
// public_key public_key::mult( const fc::sha256& digest ) const
// {
// // get point from this public key
// const EC_POINT* master_pub = EC_KEY_get0_public_key( my->_key );
// ec_group group(EC_GROUP_new_by_curve_name(NID_secp256k1));
//
// ssl_bignum z;
// BN_bin2bn((unsigned char*)&digest, sizeof(digest), z);
//
// // multiply by digest
// ssl_bignum one;
// BN_one(one);
// bn_ctx ctx(BN_CTX_new());
//
// ec_point result(EC_POINT_new(group));
// EC_POINT_mul(group, result, z, master_pub, one, ctx);
//
// public_key rtn;
// rtn.my->_key = EC_KEY_new_by_curve_name( NID_secp256k1 );
// EC_KEY_set_public_key(rtn.my->_key,result);
//
// return rtn;
// }
public_key public_key::add( const fc::sha256& digest )const
{
try {
ec_group group(EC_GROUP_new_by_curve_name(NID_secp256k1));
bn_ctx ctx(BN_CTX_new());
fc::bigint digest_bi( (char*)&digest, sizeof(digest) );
ssl_bignum order;
EC_GROUP_get_order(group, order, ctx);
if( digest_bi > fc::bigint(order) )
{
FC_THROW_EXCEPTION( exception, "digest > group order" );
}
public_key digest_key = private_key::regenerate(digest).get_public_key();
const EC_POINT* digest_point = EC_KEY_get0_public_key( digest_key.my->_key );
// get point from this public key
const EC_POINT* master_pub = EC_KEY_get0_public_key( my->_key );
// ssl_bignum z;
// BN_bin2bn((unsigned char*)&digest, sizeof(digest), z);
// multiply by digest
// ssl_bignum one;
// BN_one(one);
ec_point result(EC_POINT_new(group));
EC_POINT_add(group, result, digest_point, master_pub, ctx);
if (EC_POINT_is_at_infinity(group, result))
{
FC_THROW_EXCEPTION( exception, "point at infinity" );
}
public_key rtn;
rtn.my->_key = EC_KEY_new_by_curve_name( NID_secp256k1 );
EC_KEY_set_public_key(rtn.my->_key,result);
return rtn;
} FC_RETHROW_EXCEPTIONS( debug, "digest: ${digest}", ("digest",digest) );
}
std::string public_key::to_base58() const
{
public_key_data key = serialize();
return to_base58( key );
}
// signature private_key::sign( const fc::sha256& digest )const
// {
// unsigned int buf_len = ECDSA_size(my->_key);
//// fprintf( stderr, "%d %d\n", buf_len, sizeof(sha256) );
// signature sig;
// assert( buf_len == sizeof(sig) );
//
// if( !ECDSA_sign( 0,
// (const unsigned char*)&digest, sizeof(digest),
// (unsigned char*)&sig, &buf_len, my->_key ) )
// {
// FC_THROW_EXCEPTION( exception, "signing error" );
// }
//
//
// return sig;
// }
// bool public_key::verify( const fc::sha256& digest, const fc::ecc::signature& sig )
// {
// return 1 == ECDSA_verify( 0, (unsigned char*)&digest, sizeof(digest), (unsigned char*)&sig, sizeof(sig), my->_key );
// }
public_key_data public_key::serialize()const
{
public_key_data dat;
if( !my->_key ) return dat;
EC_KEY_set_conv_form( my->_key, POINT_CONVERSION_COMPRESSED );
/*size_t nbytes = i2o_ECPublicKey( my->_key, nullptr ); */
/*assert( nbytes == 33 )*/
char* front = &dat.data[0];
i2o_ECPublicKey( my->_key, (unsigned char**)&front ); // FIXME: questionable memory handling
return dat;
/*
EC_POINT* pub = EC_KEY_get0_public_key( my->_key );
EC_GROUP* group = EC_KEY_get0_group( my->_key );
EC_POINT_get_affine_coordinates_GFp( group, pub, self.my->_pub_x.get(), self.my->_pub_y.get(), nullptr );
*/
}
public_key_point_data public_key::serialize_ecc_point()const
{
public_key_point_data dat;
if( !my->_key ) return dat;
EC_KEY_set_conv_form( my->_key, POINT_CONVERSION_UNCOMPRESSED );
char* front = &dat.data[0];
i2o_ECPublicKey( my->_key, (unsigned char**)&front ); // FIXME: questionable memory handling
return dat;
}
public_key::public_key( const public_key_point_data& dat )
{
const char* front = &dat.data[0];
if( *front == 0 ){}
else
{
my->_key = EC_KEY_new_by_curve_name( NID_secp256k1 );
my->_key = o2i_ECPublicKey( &my->_key, (const unsigned char**)&front, sizeof(dat) );
if( !my->_key )
{
FC_THROW_EXCEPTION( exception, "error decoding public key", ("s", ERR_error_string( ERR_get_error(), nullptr) ) );
}
}
}
public_key::public_key( const public_key_data& dat )
{
const char* front = &dat.data[0];
if( *front == 0 ){}
else
{
my->_key = EC_KEY_new_by_curve_name( NID_secp256k1 );
my->_key = o2i_ECPublicKey( &my->_key, (const unsigned char**)&front, sizeof(public_key_data) );
if( !my->_key )
{
FC_THROW_EXCEPTION( exception, "error decoding public key", ("s", ERR_error_string( ERR_get_error(), nullptr) ) );
}
}
}
// bool private_key::verify( const fc::sha256& digest, const fc::ecc::signature& sig )
// {
// return 1 == ECDSA_verify( 0, (unsigned char*)&digest, sizeof(digest), (unsigned char*)&sig, sizeof(sig), my->_key );
// }
public_key::public_key( const compact_signature& c, const fc::sha256& digest, bool check_canonical )
{
int nV = c.data[0];
if (nV<27 || nV>=35)
FC_THROW_EXCEPTION( exception, "unable to reconstruct public key from signature" );
ECDSA_SIG *sig = ECDSA_SIG_new();
BN_bin2bn(&c.data[1],32,sig->r);
BN_bin2bn(&c.data[33],32,sig->s);
if( check_canonical )
{
FC_ASSERT( is_canonical( c ), "signature is not canonical" );
}
my->_key = EC_KEY_new_by_curve_name(NID_secp256k1);
if (nV >= 31)
{
EC_KEY_set_conv_form( my->_key, POINT_CONVERSION_COMPRESSED );
nV -= 4;
// fprintf( stderr, "compressed\n" );
}
if (detail::public_key_impl::ECDSA_SIG_recover_key_GFp(my->_key, sig, (unsigned char*)&digest, sizeof(digest), nV - 27, 0) == 1)
{
ECDSA_SIG_free(sig);
return;
}
ECDSA_SIG_free(sig);
FC_THROW_EXCEPTION( exception, "unable to reconstruct public key from signature" );
}
}}

View file

@ -0,0 +1,40 @@
#include <fc/crypto/elliptic.hpp>
#include <fc/crypto/base58.hpp>
#include <fc/crypto/openssl.hpp>
#include <fc/fwd_impl.hpp>
#include <fc/exception/exception.hpp>
#include <fc/log/logger.hpp>
#include <assert.h>
#include <secp256k1.h>
#include "_elliptic_impl_priv.hpp"
#include "_elliptic_impl_pub.hpp"
namespace fc { namespace ecc {
namespace detail
{
const secp256k1_context_t* _get_context() {
static secp256k1_context_t* ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN);
return ctx;
}
void _init_lib() {
static const secp256k1_context_t* ctx = _get_context();
static int init_o = init_openssl();
}
}
static const private_key_secret empty_priv;
fc::sha512 private_key::get_shared_secret( const public_key& other )const
{
FC_ASSERT( my->_key != empty_priv );
FC_ASSERT( other.my->_key != nullptr );
public_key_data pub(other.serialize());
FC_ASSERT( secp256k1_ec_pubkey_tweak_mul( detail::_get_context(), (unsigned char*) pub.begin(), pub.size(), (unsigned char*) my->_key.data() ) );
return fc::sha512::hash( pub.begin() + 1, pub.size() - 1 );
}
} }

View file

@ -0,0 +1,261 @@
#include <fc/crypto/elliptic.hpp>
#include <fc/crypto/base58.hpp>
#include <fc/crypto/openssl.hpp>
#include <fc/fwd_impl.hpp>
#include <fc/exception/exception.hpp>
#include <fc/log/logger.hpp>
#include <assert.h>
#include "_elliptic_impl_pub.hpp"
namespace fc { namespace ecc {
namespace detail
{
void _init_lib() {
static int init_o = init_openssl();
}
class private_key_impl
{
public:
private_key_impl() BOOST_NOEXCEPT
{
_init_lib();
}
private_key_impl( const private_key_impl& cpy ) BOOST_NOEXCEPT
{
_init_lib();
*this = cpy;
}
private_key_impl( private_key_impl&& cpy ) BOOST_NOEXCEPT
{
_init_lib();
*this = cpy;
}
~private_key_impl() BOOST_NOEXCEPT
{
free_key();
}
private_key_impl& operator=( const private_key_impl& pk ) BOOST_NOEXCEPT
{
if (pk._key == nullptr)
{
free_key();
} else if ( _key == nullptr ) {
_key = EC_KEY_dup( pk._key );
} else {
EC_KEY_copy( _key, pk._key );
}
return *this;
}
private_key_impl& operator=( private_key_impl&& pk ) BOOST_NOEXCEPT
{
if ( this != &pk ) {
free_key();
_key = pk._key;
pk._key = nullptr;
}
return *this;
}
EC_KEY* _key = nullptr;
private:
void free_key() BOOST_NOEXCEPT
{
if( _key != nullptr )
{
EC_KEY_free(_key);
_key = nullptr;
}
}
};
}
private_key::private_key() {}
private_key::private_key( const private_key& pk ) : my( pk.my ) {}
private_key::private_key( private_key&& pk ) : my( std::move( pk.my ) ) {}
private_key::~private_key() {}
private_key& private_key::operator=( private_key&& pk )
{
my = std::move(pk.my);
return *this;
}
private_key& private_key::operator=( const private_key& pk )
{
my = pk.my;
return *this;
}
static void * ecies_key_derivation(const void *input, size_t ilen, void *output, size_t *olen)
{
if (*olen < SHA512_DIGEST_LENGTH) {
return NULL;
}
*olen = SHA512_DIGEST_LENGTH;
return (void*)SHA512((const unsigned char*)input, ilen, (unsigned char*)output);
}
int static inline EC_KEY_regenerate_key(EC_KEY *eckey, const BIGNUM *priv_key)
{
int ok = 0;
BN_CTX *ctx = NULL;
EC_POINT *pub_key = NULL;
if (!eckey) return 0;
const EC_GROUP *group = EC_KEY_get0_group(eckey);
if ((ctx = BN_CTX_new()) == NULL)
goto err;
pub_key = EC_POINT_new(group);
if (pub_key == NULL)
goto err;
if (!EC_POINT_mul(group, pub_key, priv_key, NULL, NULL, ctx))
goto err;
EC_KEY_set_private_key(eckey,priv_key);
EC_KEY_set_public_key(eckey,pub_key);
ok = 1;
err:
if (pub_key) EC_POINT_free(pub_key);
if (ctx != NULL) BN_CTX_free(ctx);
return(ok);
}
private_key private_key::regenerate( const fc::sha256& secret )
{
private_key self;
self.my->_key = EC_KEY_new_by_curve_name( NID_secp256k1 );
if( !self.my->_key ) FC_THROW_EXCEPTION( exception, "Unable to generate EC key" );
ssl_bignum bn;
BN_bin2bn( (const unsigned char*)&secret, 32, bn );
if( !EC_KEY_regenerate_key(self.my->_key,bn) )
{
FC_THROW_EXCEPTION( exception, "unable to regenerate key" );
}
return self;
}
fc::sha256 private_key::get_secret()const
{
return get_secret( my->_key );
}
private_key::private_key( EC_KEY* k )
{
my->_key = k;
}
public_key private_key::get_public_key()const
{
public_key pub;
pub.my->_key = EC_KEY_new_by_curve_name( NID_secp256k1 );
EC_KEY_set_public_key( pub.my->_key, EC_KEY_get0_public_key( my->_key ) );
return pub;
}
fc::sha512 private_key::get_shared_secret( const public_key& other )const
{
FC_ASSERT( my->_key != nullptr );
FC_ASSERT( other.my->_key != nullptr );
fc::sha512 buf;
ECDH_compute_key( (unsigned char*)&buf, sizeof(buf), EC_KEY_get0_public_key(other.my->_key), my->_key, ecies_key_derivation );
return buf;
}
compact_signature private_key::sign_compact( const fc::sha256& digest )const
{
try {
FC_ASSERT( my->_key != nullptr );
auto my_pub_key = get_public_key().serialize(); // just for good measure
//ECDSA_SIG *sig = ECDSA_do_sign((unsigned char*)&digest, sizeof(digest), my->_key);
public_key_data key_data;
while( true )
{
ecdsa_sig sig = ECDSA_do_sign((unsigned char*)&digest, sizeof(digest), my->_key);
if (sig==nullptr)
FC_THROW_EXCEPTION( exception, "Unable to sign" );
compact_signature csig;
// memset( csig.data, 0, sizeof(csig) );
int nBitsR = BN_num_bits(sig->r);
int nBitsS = BN_num_bits(sig->s);
if (nBitsR <= 256 && nBitsS <= 256)
{
int nRecId = -1;
EC_KEY* key = EC_KEY_new_by_curve_name( NID_secp256k1 );
FC_ASSERT( key );
EC_KEY_set_conv_form( key, POINT_CONVERSION_COMPRESSED );
for (int i=0; i<4; i++)
{
if (detail::public_key_impl::ECDSA_SIG_recover_key_GFp(key, sig, (unsigned char*)&digest, sizeof(digest), i, 1) == 1)
{
unsigned char* buffer = (unsigned char*) key_data.begin();
i2o_ECPublicKey( key, &buffer ); // FIXME: questionable memory handling
if ( key_data == my_pub_key )
{
nRecId = i;
break;
}
}
}
EC_KEY_free( key );
if (nRecId == -1)
{
FC_THROW_EXCEPTION( exception, "unable to construct recoverable key");
}
unsigned char* result = nullptr;
auto bytes = i2d_ECDSA_SIG( sig, &result );
auto lenR = result[3];
auto lenS = result[5+lenR];
//idump( (result[0])(result[1])(result[2])(result[3])(result[3+lenR])(result[4+lenR])(bytes)(lenR)(lenS) );
if( lenR != 32 ) { free(result); continue; }
if( lenS != 32 ) { free(result); continue; }
//idump( (33-(nBitsR+7)/8) );
//idump( (65-(nBitsS+7)/8) );
//idump( (sizeof(csig) ) );
memcpy( &csig.data[1], &result[4], lenR );
memcpy( &csig.data[33], &result[6+lenR], lenS );
//idump( (csig.data[33]) );
//idump( (csig.data[1]) );
free(result);
//idump( (nRecId) );
csig.data[0] = nRecId+27+4;//(fCompressedPubKey ? 4 : 0);
/*
idump( (csig) );
auto rlen = BN_bn2bin(sig->r,&csig.data[33-(nBitsR+7)/8]);
auto slen = BN_bn2bin(sig->s,&csig.data[65-(nBitsS+7)/8]);
idump( (rlen)(slen) );
*/
}
return csig;
} // while true
} FC_RETHROW_EXCEPTIONS( warn, "sign ${digest}", ("digest", digest)("private_key",*this) );
}
} }

View file

@ -0,0 +1,152 @@
#include <fc/crypto/elliptic.hpp>
#include <fc/crypto/base58.hpp>
#include <fc/crypto/openssl.hpp>
#include <fc/fwd_impl.hpp>
#include <fc/exception/exception.hpp>
#include <fc/log/logger.hpp>
#include <assert.h>
#include <secp256k1.h>
#include "_elliptic_impl_priv.hpp"
namespace fc { namespace ecc {
namespace detail
{
const secp256k1_context_t* _get_context() {
static secp256k1_context_t* ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN);
return ctx;
}
void _init_lib() {
static const secp256k1_context_t* ctx = _get_context();
static int init_o = init_openssl();
}
class public_key_impl
{
public:
public_key_impl() noexcept
{
_init_lib();
}
public_key_impl( const public_key_impl& cpy ) noexcept
: _key( cpy._key )
{
_init_lib();
}
public_key_data _key;
};
}
static const public_key_data empty_pub;
static const private_key_secret empty_priv;
fc::sha512 private_key::get_shared_secret( const public_key& other )const
{
FC_ASSERT( my->_key != empty_priv );
FC_ASSERT( other.my->_key != empty_pub );
public_key_data pub(other.my->_key);
FC_ASSERT( secp256k1_ec_pubkey_tweak_mul( detail::_get_context(), (unsigned char*) pub.begin(), pub.size(), (unsigned char*) my->_key.data() ) );
return fc::sha512::hash( pub.begin() + 1, pub.size() - 1 );
}
public_key::public_key() {}
public_key::public_key( const public_key &pk ) : my( pk.my ) {}
public_key::public_key( public_key &&pk ) : my( std::move( pk.my ) ) {}
public_key::~public_key() {}
public_key& public_key::operator=( const public_key& pk )
{
my = pk.my;
return *this;
}
public_key& public_key::operator=( public_key&& pk )
{
my = pk.my;
return *this;
}
bool public_key::valid()const
{
return my->_key != empty_pub;
}
public_key public_key::add( const fc::sha256& digest )const
{
FC_ASSERT( my->_key != empty_pub );
public_key_data new_key;
memcpy( new_key.begin(), my->_key.begin(), new_key.size() );
FC_ASSERT( secp256k1_ec_pubkey_tweak_add( detail::_get_context(), (unsigned char*) new_key.begin(), new_key.size(), (unsigned char*) digest.data() ) );
return public_key( new_key );
}
std::string public_key::to_base58() const
{
FC_ASSERT( my->_key != empty_pub );
return to_base58( my->_key );
}
public_key_data public_key::serialize()const
{
FC_ASSERT( my->_key != empty_pub );
return my->_key;
}
public_key_point_data public_key::serialize_ecc_point()const
{
FC_ASSERT( my->_key != empty_pub );
public_key_point_data dat;
unsigned int pk_len = my->_key.size();
memcpy( dat.begin(), my->_key.begin(), pk_len );
FC_ASSERT( secp256k1_ec_pubkey_decompress( detail::_get_context(), (unsigned char *) dat.begin(), (int*) &pk_len ) );
FC_ASSERT( pk_len == dat.size() );
return dat;
}
public_key::public_key( const public_key_point_data& dat )
{
const char* front = &dat.data[0];
if( *front == 0 ){}
else
{
EC_KEY *key = EC_KEY_new_by_curve_name( NID_secp256k1 );
key = o2i_ECPublicKey( &key, (const unsigned char**)&front, sizeof(dat) );
FC_ASSERT( key );
EC_KEY_set_conv_form( key, POINT_CONVERSION_COMPRESSED );
unsigned char* buffer = (unsigned char*) my->_key.begin();
i2o_ECPublicKey( key, &buffer ); // FIXME: questionable memory handling
EC_KEY_free( key );
}
}
public_key::public_key( const public_key_data& dat )
{
my->_key = dat;
}
public_key::public_key( const compact_signature& c, const fc::sha256& digest, bool check_canonical )
{
int nV = c.data[0];
if (nV<27 || nV>=35)
FC_THROW_EXCEPTION( exception, "unable to reconstruct public key from signature" );
if( check_canonical )
{
FC_ASSERT( is_canonical( c ), "signature is not canonical" );
}
unsigned int pk_len;
FC_ASSERT( secp256k1_ecdsa_recover_compact( detail::_get_context(), (unsigned char*) digest.data(), (unsigned char*) c.begin() + 1, (unsigned char*) my->_key.begin(), (int*) &pk_len, 1, (*c.begin() - 27) & 3 ) );
FC_ASSERT( pk_len == my->_key.size() );
}
} }

View file

@ -436,8 +436,30 @@ namespace fc
return variant();
}
/** the purpose of this check is to verify that we will not get a stack overflow in the recursive descent parser */
void check_string_depth( const string& utf8_str )
{
int32_t open_object = 0;
int32_t open_array = 0;
for( auto c : utf8_str )
{
switch( c )
{
case '{': open_object++; break;
case '}': open_object--; break;
case '[': open_array++; break;
case ']': open_array--; break;
default: break;
}
FC_ASSERT( open_object < 100 && open_array < 100, "object graph too deep", ("object depth",open_object)("array depth", open_array) );
}
}
variant json::from_string( const std::string& utf8_str, parse_type ptype )
{ try {
check_string_depth( utf8_str );
fc::stringstream in( utf8_str );
//in.exceptions( std::ifstream::eofbit );
switch( ptype )
@ -457,6 +479,7 @@ namespace fc
variants json::variants_from_string( const std::string& utf8_str, parse_type ptype )
{ try {
check_string_depth( utf8_str );
variants result;
fc::stringstream in( utf8_str );
//in.exceptions( std::ifstream::eofbit );
@ -567,13 +590,26 @@ namespace fc
os << "null";
return;
case variant::int64_type:
os << v.as_int64();
{
int64_t i = v.as_int64();
if( i > 0xffffffff )
os << '"'<<v.as_string()<<'"';
else
os << i;
return;
}
case variant::uint64_type:
os << v.as_uint64();
{
uint64_t i = v.as_uint64();
if( i > 0xffffffff )
os << '"'<<v.as_string()<<'"';
else
os << i;
return;
}
case variant::double_type:
os << v.as_double();
//os << v.as_string();
os << '"'<<v.as_string()<<'"';
return;
case variant::bool_type:
os << v.as_string();

View file

@ -11,6 +11,11 @@
#include <fc/thread/thread.hpp>
#include <fc/asio.hpp>
#ifdef DEFAULT_LOGGER
# undef DEFAULT_LOGGER
#endif
#define DEFAULT_LOGGER "rpc"
namespace fc { namespace http {
namespace detail {
@ -57,22 +62,105 @@ namespace fc { namespace http {
static const long timeout_open_handshake = 0;
};
struct asio_tls_with_stub_log : public websocketpp::config::asio_tls {
typedef asio_with_stub_log type;
typedef asio_tls base;
typedef base::concurrency_type concurrency_type;
typedef base::request_type request_type;
typedef base::response_type response_type;
typedef base::message_type message_type;
typedef base::con_msg_manager_type con_msg_manager_type;
typedef base::endpoint_msg_manager_type endpoint_msg_manager_type;
/// Custom Logging policies
/*typedef websocketpp::log::syslog<concurrency_type,
websocketpp::log::elevel> elog_type;
typedef websocketpp::log::syslog<concurrency_type,
websocketpp::log::alevel> alog_type;
*/
//typedef base::alog_type alog_type;
//typedef base::elog_type elog_type;
typedef websocketpp::log::stub elog_type;
typedef websocketpp::log::stub alog_type;
typedef base::rng_type rng_type;
struct transport_config : public base::transport_config {
typedef type::concurrency_type concurrency_type;
typedef type::alog_type alog_type;
typedef type::elog_type elog_type;
typedef type::request_type request_type;
typedef type::response_type response_type;
typedef websocketpp::transport::asio::tls_socket::endpoint socket_type;
};
typedef websocketpp::transport::asio::endpoint<transport_config>
transport_type;
static const long timeout_open_handshake = 0;
};
struct asio_tls_stub_log : public websocketpp::config::asio_tls {
typedef asio_tls_stub_log type;
typedef asio_tls base;
typedef base::concurrency_type concurrency_type;
typedef base::request_type request_type;
typedef base::response_type response_type;
typedef base::message_type message_type;
typedef base::con_msg_manager_type con_msg_manager_type;
typedef base::endpoint_msg_manager_type endpoint_msg_manager_type;
//typedef base::alog_type alog_type;
//typedef base::elog_type elog_type;
typedef websocketpp::log::stub elog_type;
typedef websocketpp::log::stub alog_type;
typedef base::rng_type rng_type;
struct transport_config : public base::transport_config {
typedef type::concurrency_type concurrency_type;
typedef type::alog_type alog_type;
typedef type::elog_type elog_type;
typedef type::request_type request_type;
typedef type::response_type response_type;
typedef websocketpp::transport::asio::tls_socket::endpoint socket_type;
};
typedef websocketpp::transport::asio::endpoint<transport_config>
transport_type;
};
using websocketpp::connection_hdl;
typedef websocketpp::server<asio_with_stub_log> websocket_server_type;
typedef websocketpp::server<asio_tls_stub_log> websocket_tls_server_type;
template<typename T>
class websocket_connection_impl : public websocket_connection
{
public:
websocket_connection_impl( T con )
:_ws_connection(con){}
:_ws_connection(con){
wdump((uint64_t(this)));
}
~websocket_connection_impl()
{
wdump((uint64_t(this)));
}
virtual void send_message( const std::string& message )override
{
idump((message));
auto ec = _ws_connection->send( message );
FC_ASSERT( !ec, "websocket send failed: ${msg}", ("msg",ec.message() ) );
}
@ -84,12 +172,15 @@ namespace fc { namespace http {
T _ws_connection;
};
typedef websocketpp::lib::shared_ptr<boost::asio::ssl::context> context_ptr;
class websocket_server_impl
{
public:
websocket_server_impl()
:_server_thread( fc::thread::current() )
{
_server.clear_access_channels( websocketpp::log::alevel::all );
_server.init_asio(&fc::asio::default_io_service());
_server.set_reuse_addr(true);
@ -103,13 +194,39 @@ namespace fc { namespace http {
_server_thread.async( [&](){
auto current_con = _connections.find(hdl);
assert( current_con != _connections.end() );
//wdump(("server")(msg->get_payload()));
wdump(("server")(msg->get_payload()));
current_con->second->on_message( msg->get_payload() );
}).wait();
});
_server.set_http_handler( [&]( connection_hdl hdl ){
_server_thread.async( [&](){
auto current_con = std::make_shared<websocket_connection_impl<websocket_server_type::connection_ptr>>( _server.get_con_from_hdl(hdl) );
_on_connection( current_con );
auto con = _server.get_con_from_hdl(hdl);
wdump(("server")(con->get_request_body()));
auto response = current_con->on_http( con->get_request_body() );
con->set_body( response );
con->set_status( websocketpp::http::status_code::ok );
current_con->closed();
}).wait();
});
_server.set_close_handler( [&]( connection_hdl hdl ){
_server_thread.async( [&](){
if( _connections.find(hdl) != _connections.end() )
{
_connections[hdl]->closed();
_connections.erase( hdl );
}
else
{
wlog( "unknown connection closed" );
}
}).wait();
});
@ -117,7 +234,15 @@ namespace fc { namespace http {
if( _server.is_listening() )
{
_server_thread.async( [&](){
if( _connections.find(hdl) != _connections.end() )
{
_connections[hdl]->closed();
_connections.erase( hdl );
}
else
{
wlog( "unknown connection failed" );
}
}).wait();
}
});
@ -140,8 +265,126 @@ namespace fc { namespace http {
fc::promise<void>::ptr _closed;
};
class websocket_tls_server_impl
{
public:
websocket_tls_server_impl( const string& server_pem, const string& ssl_password )
:_server_thread( fc::thread::current() )
{
//if( server_pem.size() )
{
_server.set_tls_init_handler( [=]( websocketpp::connection_hdl hdl ) -> context_ptr {
context_ptr ctx = websocketpp::lib::make_shared<boost::asio::ssl::context>(boost::asio::ssl::context::tlsv1);
try {
ctx->set_options(boost::asio::ssl::context::default_workarounds |
boost::asio::ssl::context::no_sslv2 |
boost::asio::ssl::context::no_sslv3 |
boost::asio::ssl::context::single_dh_use);
ctx->set_password_callback([=](std::size_t max_length, boost::asio::ssl::context::password_purpose){ return ssl_password;});
ctx->use_certificate_chain_file(server_pem);
ctx->use_private_key_file(server_pem, boost::asio::ssl::context::pem);
} catch (std::exception& e) {
std::cout << e.what() << std::endl;
}
return ctx;
});
}
_server.clear_access_channels( websocketpp::log::alevel::all );
_server.init_asio(&fc::asio::default_io_service());
_server.set_reuse_addr(true);
_server.set_open_handler( [&]( connection_hdl hdl ){
_server_thread.async( [&](){
auto new_con = std::make_shared<websocket_connection_impl<websocket_tls_server_type::connection_ptr>>( _server.get_con_from_hdl(hdl) );
_on_connection( _connections[hdl] = new_con );
}).wait();
});
_server.set_message_handler( [&]( connection_hdl hdl, websocket_server_type::message_ptr msg ){
_server_thread.async( [&](){
auto current_con = _connections.find(hdl);
assert( current_con != _connections.end() );
wdump(("server")(msg->get_payload()));
current_con->second->on_message( msg->get_payload() );
}).wait();
});
_server.set_http_handler( [&]( connection_hdl hdl ){
_server_thread.async( [&](){
auto current_con = std::make_shared<websocket_connection_impl<websocket_tls_server_type::connection_ptr>>( _server.get_con_from_hdl(hdl) );
try{
_on_connection( current_con );
auto con = _server.get_con_from_hdl(hdl);
wdump(("server")(con->get_request_body()));
auto response = current_con->on_http( con->get_request_body() );
con->set_body( response );
con->set_status( websocketpp::http::status_code::ok );
} catch ( const fc::exception& e )
{
edump((e.to_detail_string()));
}
current_con->closed();
}).wait();
});
_server.set_close_handler( [&]( connection_hdl hdl ){
_server_thread.async( [&](){
_connections[hdl]->closed();
_connections.erase( hdl );
}).wait();
});
_server.set_fail_handler( [&]( connection_hdl hdl ){
if( _server.is_listening() )
{
_server_thread.async( [&](){
if( _connections.find(hdl) != _connections.end() )
{
_connections[hdl]->closed();
_connections.erase( hdl );
}
}).wait();
}
});
}
~websocket_tls_server_impl()
{
if( _server.is_listening() )
_server.stop_listening();
auto cpy_con = _connections;
for( auto item : cpy_con )
_server.close( item.first, 0, "server exit" );
}
typedef std::map<connection_hdl, websocket_connection_ptr,std::owner_less<connection_hdl> > con_map;
con_map _connections;
fc::thread& _server_thread;
websocket_tls_server_type _server;
on_connection_handler _on_connection;
fc::promise<void>::ptr _closed;
};
typedef websocketpp::client<asio_with_stub_log> websocket_client_type;
typedef websocketpp::client<asio_tls_stub_log> websocket_tls_client_type;
typedef websocket_client_type::connection_ptr websocket_client_connection_type;
typedef websocket_tls_client_type::connection_ptr websocket_tls_client_connection_type;
class websocket_client_impl
{
@ -154,13 +397,13 @@ namespace fc { namespace http {
_client.clear_access_channels( websocketpp::log::alevel::all );
_client.set_message_handler( [&]( connection_hdl hdl, message_ptr msg ){
_client_thread.async( [&](){
// wdump((msg->get_payload()));
wdump((msg->get_payload()));
if( _connection )
_connection->on_message( msg->get_payload() );
}).wait();
});
_client.set_close_handler( [=]( connection_hdl hdl ){
if( _connection )
_client_thread.async( [&](){ if( _connection ) _connection->closed(); _connection.reset(); } ).wait();
_client_thread.async( [&](){ if( _connection ) {_connection->closed(); _connection.reset();} } ).wait();
if( _closed ) _closed->set_value();
});
_client.set_fail_handler( [=]( connection_hdl hdl ){
@ -173,6 +416,7 @@ namespace fc { namespace http {
if( _closed )
_closed->set_value();
});
_client.init_asio( &fc::asio::default_io_service() );
}
~websocket_client_impl()
@ -180,6 +424,7 @@ namespace fc { namespace http {
if(_connection )
{
_connection->close(0, "client closed");
_connection.reset();
_closed->wait();
}
}
@ -189,6 +434,88 @@ namespace fc { namespace http {
websocket_client_type _client;
websocket_connection_ptr _connection;
};
class websocket_tls_client_impl
{
public:
typedef websocket_tls_client_type::message_ptr message_ptr;
websocket_tls_client_impl()
:_client_thread( fc::thread::current() )
{
_client.clear_access_channels( websocketpp::log::alevel::all );
_client.set_message_handler( [&]( connection_hdl hdl, message_ptr msg ){
_client_thread.async( [&](){
wdump((msg->get_payload()));
_connection->on_message( msg->get_payload() );
}).wait();
});
_client.set_close_handler( [=]( connection_hdl hdl ){
if( _connection )
{
try {
_client_thread.async( [&](){
wlog(". ${p}", ("p",uint64_t(_connection.get())));
if( !_shutting_down && !_closed && _connection )
_connection->closed();
_connection.reset();
} ).wait();
} catch ( const fc::exception& e )
{
if( _closed ) _closed->set_exception( e.dynamic_copy_exception() );
}
if( _closed ) _closed->set_value();
}
});
_client.set_fail_handler( [=]( connection_hdl hdl ){
elog( "." );
auto con = _client.get_con_from_hdl(hdl);
auto message = con->get_ec().message();
if( _connection )
_client_thread.async( [&](){ if( _connection ) _connection->closed(); _connection.reset(); } ).wait();
if( _connected && !_connected->ready() )
_connected->set_exception( exception_ptr( new FC_EXCEPTION( exception, "${message}", ("message",message)) ) );
if( _closed )
_closed->set_value();
});
_client.set_tls_init_handler( [=](websocketpp::connection_hdl) {
context_ptr ctx = websocketpp::lib::make_shared<boost::asio::ssl::context>(boost::asio::ssl::context::tlsv1);
try {
ctx->set_options(boost::asio::ssl::context::default_workarounds |
boost::asio::ssl::context::no_sslv2 |
boost::asio::ssl::context::no_sslv3 |
boost::asio::ssl::context::single_dh_use);
} catch (std::exception& e) {
edump((e.what()));
std::cout << e.what() << std::endl;
}
return ctx;
});
_client.init_asio( &fc::asio::default_io_service() );
}
~websocket_tls_client_impl()
{
if(_connection )
{
wlog(".");
_shutting_down = true;
_connection->close(0, "client closed");
_closed->wait();
}
}
bool _shutting_down = false;
fc::promise<void>::ptr _connected;
fc::promise<void>::ptr _closed;
fc::thread& _client_thread;
websocket_tls_client_type _client;
websocket_connection_ptr _connection;
};
} // namespace detail
websocket_server::websocket_server():my( new detail::websocket_server_impl() ) {}
@ -212,10 +539,45 @@ namespace fc { namespace http {
my->_server.start_accept();
}
websocket_client::websocket_client():my( new detail::websocket_client_impl() ) {}
websocket_tls_server::websocket_tls_server( const string& server_pem, const string& ssl_password ):my( new detail::websocket_tls_server_impl(server_pem, ssl_password) ) {}
websocket_tls_server::~websocket_tls_server(){}
void websocket_tls_server::on_connection( const on_connection_handler& handler )
{
my->_on_connection = handler;
}
void websocket_tls_server::listen( uint16_t port )
{
my->_server.listen(port);
}
void websocket_tls_server::listen( const fc::ip::endpoint& ep )
{
my->_server.listen( boost::asio::ip::tcp::endpoint( boost::asio::ip::address_v4(uint32_t(ep.get_address())),ep.port()) );
}
void websocket_tls_server::start_accept() {
my->_server.start_accept();
}
websocket_tls_client::websocket_tls_client():my( new detail::websocket_tls_client_impl() ) {}
websocket_tls_client::~websocket_tls_client(){ }
websocket_client::websocket_client():my( new detail::websocket_client_impl() ),smy(new detail::websocket_tls_client_impl()) {}
websocket_client::~websocket_client(){ }
websocket_connection_ptr websocket_client::connect( const std::string& uri )
{ try {
if( uri.substr(0,4) == "wss:" )
return secure_connect(uri);
FC_ASSERT( uri.substr(0,3) == "ws:" );
// wlog( "connecting to ${uri}", ("uri",uri));
websocketpp::lib::error_code ec;
@ -228,6 +590,54 @@ namespace fc { namespace http {
my->_connected->set_value();
});
auto con = my->_client.get_connection( uri, ec );
if( ec ) FC_ASSERT( !ec, "error: ${e}", ("e",ec.message()) );
my->_client.connect(con);
my->_connected->wait();
return my->_connection;
} FC_CAPTURE_AND_RETHROW( (uri) ) }
websocket_connection_ptr websocket_client::secure_connect( const std::string& uri )
{ try {
if( uri.substr(0,3) == "ws:" )
return connect(uri);
FC_ASSERT( uri.substr(0,4) == "wss:" );
// wlog( "connecting to ${uri}", ("uri",uri));
websocketpp::lib::error_code ec;
smy->_connected = fc::promise<void>::ptr( new fc::promise<void>("websocket::connect") );
smy->_client.set_open_handler( [=]( websocketpp::connection_hdl hdl ){
auto con = smy->_client.get_con_from_hdl(hdl);
smy->_connection = std::make_shared<detail::websocket_connection_impl<detail::websocket_tls_client_connection_type>>( con );
smy->_closed = fc::promise<void>::ptr( new fc::promise<void>("websocket::closed") );
smy->_connected->set_value();
});
auto con = smy->_client.get_connection( uri, ec );
if( ec )
FC_ASSERT( !ec, "error: ${e}", ("e",ec.message()) );
smy->_client.connect(con);
smy->_connected->wait();
return smy->_connection;
} FC_CAPTURE_AND_RETHROW( (uri) ) }
websocket_connection_ptr websocket_tls_client::connect( const std::string& uri )
{ try {
// wlog( "connecting to ${uri}", ("uri",uri));
websocketpp::lib::error_code ec;
my->_connected = fc::promise<void>::ptr( new fc::promise<void>("websocket::connect") );
my->_client.set_open_handler( [=]( websocketpp::connection_hdl hdl ){
auto con = my->_client.get_con_from_hdl(hdl);
my->_connection = std::make_shared<detail::websocket_connection_impl<detail::websocket_tls_client_connection_type>>( con );
my->_closed = fc::promise<void>::ptr( new fc::promise<void>("websocket::closed") );
my->_connected->set_value();
});
auto con = my->_client.get_connection( uri, ec );
if( ec )
{

72
src/rpc/cli.cpp Normal file
View file

@ -0,0 +1,72 @@
#include <fc/rpc/cli.hpp>
#include <fc/thread/thread.hpp>
#include <iostream>
#ifndef WIN32
#include <unistd.h>
#endif
#ifdef HAVE_READLINE
# include <readline/readline.h>
# include <readline/history.h>
// I don't know exactly what version of readline we need. I know the 4.2 version that ships on some macs is
// missing some functions we require. We're developing against 6.3, but probably anything in the 6.x
// series is fine
# if RL_VERSION_MAJOR < 6
# ifdef _MSC_VER
# pragma message("You have an old version of readline installed that might not support some of the features we need")
# pragma message("Readline support will not be compiled in")
# else
# warning "You have an old version of readline installed that might not support some of the features we need"
# warning "Readline support will not be compiled in"
# endif
# undef HAVE_READLINE
# endif
# ifdef WIN32
# include <io.h>
# endif
#endif
namespace fc { namespace rpc {
void cli::getline( const fc::string& prompt, fc::string& line)
{
// getting file descriptor for C++ streams is near impossible
// so we just assume it's the same as the C stream...
#ifdef HAVE_READLINE
#ifndef WIN32
if( isatty( fileno( stdin ) ) )
#else
// it's implied by
// https://msdn.microsoft.com/en-us/library/f4s0ddew.aspx
// that this is the proper way to do this on Windows, but I have
// no access to a Windows compiler and thus,
// no idea if this actually works
if( _isatty( _fileno( stdin ) ) )
#endif
{
static fc::thread getline_thread("getline");
getline_thread.async( [&](){
char* line_read = nullptr;
std::cout.flush(); //readline doesn't use cin, so we must manually flush _out
line_read = readline(prompt.c_str());
if( line_read == nullptr )
FC_THROW_EXCEPTION( fc::eof_exception, "" );
if( *line_read )
add_history(line_read);
line = line_read;
free(line_read);
}).wait();
}
else
#endif
{
std::cout << prompt;
// sync_call( cin_thread, [&](){ std::getline( *input_stream, line ); }, "getline");
fc::getline( fc::cin, line );
return;
}
}
} }

View file

@ -9,9 +9,10 @@ class calculator
int32_t add( int32_t a, int32_t b ); // not implemented
int32_t sub( int32_t a, int32_t b ); // not implemented
void on_result( const std::function<void(int32_t)>& cb );
void on_result2( const std::function<void(int32_t)>& cb, int test );
};
FC_API( calculator, (add)(sub)(on_result) )
FC_API( calculator, (add)(sub)(on_result)(on_result2) )
class login_api
@ -35,6 +36,7 @@ class some_calculator
int32_t add( int32_t a, int32_t b ) { wlog("."); if( _cb ) _cb(a+b); return a+b; }
int32_t sub( int32_t a, int32_t b ) { wlog(".");if( _cb ) _cb(a-b); return a-b; }
void on_result( const std::function<void(int32_t)>& cb ) { wlog( "set callback" ); _cb = cb; return ; }
void on_result2( const std::function<void(int32_t)>& cb, int test ){}
std::function<void(int32_t)> _cb;
};
class variant_calculator
@ -43,6 +45,7 @@ class variant_calculator
double add( fc::variant a, fc::variant b ) { return a.as_double()+b.as_double(); }
double sub( fc::variant a, fc::variant b ) { return a.as_double()-b.as_double(); }
void on_result( const std::function<void(int32_t)>& cb ) { wlog("set callback"); _cb = cb; return ; }
void on_result2( const std::function<void(int32_t)>& cb, int test ){}
std::function<void(int32_t)> _cb;
};

141
tests/bloom_test.cpp Normal file
View file

@ -0,0 +1,141 @@
#include <fc/bloom_filter.hpp>
#include <fc/exception/exception.hpp>
#include <fc/reflect/variant.hpp>
#include <iostream>
#include <fc/variant.hpp>
#include <fc/io/raw.hpp>
#include <fstream>
#include <fc/io/json.hpp>
#include <fc/crypto/base64.hpp>
using namespace fc;
int main( int argc, char** argv )
{
try {
bloom_parameters parameters;
// How many elements roughly do we expect to insert?
parameters.projected_element_count = 100000;
// Maximum tolerable false positive probability? (0,1)
parameters.false_positive_probability = 0.0001; // 1 in 10000
// Simple randomizer (optional)
parameters.random_seed = 0xA5A5A5A5;
if (!parameters)
{
std::cout << "Error - Invalid set of bloom filter parameters!" << std::endl;
return 1;
}
parameters.compute_optimal_parameters();
//Instantiate Bloom Filter
bloom_filter filter(parameters);
if( argc > 1 )
{
uint32_t count = 0;
std::string line;
std::ifstream in(argv[1]);
std::ofstream words("words.txt");
while( !in.eof() && count < 100000 )
{
std::getline(in, line);
std::cout << "'"<<line<<"'\n";
if( !filter.contains(line) )
{
filter.insert( line );
words << line << "\n";
++count;
}
}
wdump((filter));
auto packed_filter = fc::raw::pack(filter);
wdump((packed_filter.size()));
wdump((packed_filter));
std::ofstream out(argv[2]);
std::string str = fc::json::to_string(packed_filter);
auto b64 = fc::base64_encode( packed_filter.data(), packed_filter.size() );
for( uint32_t i = 0; i < b64.size(); i += 1024 )
out << '"' << b64.substr( i, 1024 ) << "\",\n";
return 0;
}
std::string str_list[] = { "AbC", "iJk", "XYZ" };
// Insert into Bloom Filter
{
// Insert some strings
for (std::size_t i = 0; i < (sizeof(str_list) / sizeof(std::string)); ++i)
{
filter.insert(str_list[i]);
}
// Insert some numbers
for (std::size_t i = 0; i < 100; ++i)
{
filter.insert(i);
}
}
// Query Bloom Filter
{
// Query the existence of strings
for (std::size_t i = 0; i < (sizeof(str_list) / sizeof(std::string)); ++i)
{
if (filter.contains(str_list[i]))
{
std::cout << "BF contains: " << str_list[i] << std::endl;
}
}
// Query the existence of numbers
for (std::size_t i = 0; i < 100; ++i)
{
if (filter.contains(i))
{
std::cout << "BF contains: " << i << std::endl;
}
}
std::string invalid_str_list[] = { "AbCX", "iJkX", "XYZX" };
// Query the existence of invalid strings
for (std::size_t i = 0; i < (sizeof(invalid_str_list) / sizeof(std::string)); ++i)
{
if (filter.contains(invalid_str_list[i]))
{
std::cout << "BF falsely contains: " << invalid_str_list[i] << std::endl;
}
}
// Query the existence of invalid numbers
for (int i = -1; i > -100; --i)
{
if (filter.contains(i))
{
std::cout << "BF falsely contains: " << i << std::endl;
}
}
}
wdump((filter));
auto packed_filter = fc::raw::pack(filter);
wdump((packed_filter.size()));
wdump((packed_filter));
return 0;
}
catch ( const fc::exception& e )
{
edump((e.to_detail_string()) );
}
}

48
tests/ecc-interop.sh Executable file
View file

@ -0,0 +1,48 @@
#!/bin/sh
#TIME=time
cd "`dirname $0`"/..
echo Building ecc_test with openssl...
(
cmake -D ECC_IMPL=openssl .
make ecc_test
mv ecc_test ecc_test.openssl
) >/dev/null 2>&1
echo Building ecc_test with secp256k1...
(
cmake -D ECC_IMPL=secp256k1 .
make ecc_test
mv ecc_test ecc_test.secp256k1
) >/dev/null 2>&1
echo Building ecc_test with mixed...
(
cmake -D ECC_IMPL=mixed .
make ecc_test
mv ecc_test ecc_test.mixed
) >/dev/null 2>&1
run () {
echo "Running ecc_test.$1 test ecc.interop.$2 ..."
$TIME "./ecc_test.$1" test "ecc.interop.$2"
}
run openssl openssl
run openssl openssl
run secp256k1 secp256k1
run secp256k1 secp256k1
run mixed mixed
run mixed mixed
run openssl secp256k1
run openssl mixed
run secp256k1 openssl
run secp256k1 mixed
run mixed openssl
run mixed secp256k1
echo Done.
rm -f ecc_test.openssl ecc_test.secp256k1 ecc_test.mixed ecc.interop.openssl ecc.interop.secp256k1 ecc.interop.mixed

View file

@ -1,9 +1,75 @@
#include <fc/crypto/elliptic.hpp>
#include <fc/exception/exception.hpp>
#include <iostream>
#include <fstream>
static std::fstream interop_data;
static bool write_mode = false;
static void interop_do(const char * const data, size_t len) {
static char buffer[256];
if (!interop_data.is_open()) { return; }
FC_ASSERT(len < sizeof(buffer));
if (write_mode) {
interop_data.write(data, len);
return;
}
interop_data.read(buffer, len);
FC_ASSERT(!interop_data.eof());
FC_ASSERT(!memcmp(data, buffer, len));
}
static void interop_do(const fc::ecc::public_key_data &data) {
interop_do(data.begin(), data.size());
}
static void interop_do(const fc::ecc::private_key_secret &data) {
interop_do(data.data(), 32);
}
static void interop_do(const fc::ecc::public_key_point_data &data) {
interop_do(data.begin(), data.size());
}
static void interop_do(const std::string &data) {
interop_do(data.c_str(), data.length());
}
static void interop_do(const fc::sha512 &data) {
interop_do(data.data(), 64);
}
static void interop_do(fc::ecc::compact_signature &data) {
if (write_mode) {
interop_data.write((char*) data.begin(), data.size());
return;
}
interop_data.read((char*) data.begin(), data.size());
}
static void interop_file(const char * const name) {
interop_data.open(name, std::fstream::in | std::fstream::binary);
if (!interop_data.fail()) { return; }
write_mode = true;
interop_data.open(name, std::fstream::out | std::fstream::binary);
if (!interop_data.fail()) { return; }
std::cerr << "Can't read nor write " << name << "\n";
}
int main( int argc, char** argv )
{
if (argc > 2) {
interop_file(argv[2]);
}
fc::ecc::private_key nullkey;
for( uint32_t i = 0; i < 3000; ++ i )
{
try {
@ -12,16 +78,34 @@ int main( int argc, char** argv )
std::string pass(argv[1]);
fc::sha256 h = fc::sha256::hash( pass.c_str(), pass.size() );
fc::ecc::private_key priv = fc::ecc::private_key::generate_from_seed(h);
FC_ASSERT( nullkey != priv );
interop_do(priv.get_secret());
fc::ecc::public_key pub = priv.get_public_key();
interop_do(pub.serialize());
interop_do(pub.serialize_ecc_point());
pass += "1";
fc::sha256 h2 = fc::sha256::hash( pass.c_str(), pass.size() );
fc::ecc::public_key pub1 = pub.mult( h2 );
fc::ecc::public_key pub1 = pub.add( h2 );
interop_do(pub1.serialize());
interop_do(pub1.serialize_ecc_point());
fc::ecc::private_key priv1 = fc::ecc::private_key::generate_from_seed(h, h2);
interop_do(priv1.get_secret());
std::string b58 = pub1.to_base58();
interop_do(b58);
fc::ecc::public_key pub2 = fc::ecc::public_key::from_base58(b58);
FC_ASSERT( pub1 == pub2 );
fc::sha512 shared = priv1.get_shared_secret( pub );
interop_do(shared);
auto sig = priv.sign_compact( h );
interop_do(sig);
auto recover = fc::ecc::public_key( sig, h );
FC_ASSERT( recover == priv.get_public_key() );
interop_do(recover.serialize());
interop_do(recover.serialize_ecc_point());
FC_ASSERT( recover == pub );
}
catch ( const fc::exception& e )
{
@ -29,6 +113,9 @@ int main( int argc, char** argv )
}
}
if (interop_data.is_open()) {
interop_data.close();
}
return 0;
}

BIN
tests/ecc_test.interop.data Normal file

Binary file not shown.