Merkle trees: add OpenSSL and mbedTLS hash functions and switch History to full SHA256 (#2031)

Co-authored-by: Amaury Chamayou <amchamay@microsoft.com>
This commit is contained in:
Christoph M. Wintersteiger 2020-12-17 20:30:27 +00:00 коммит произвёл GitHub
Родитель c27ecbcb34
Коммит 7d11a18993
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
12 изменённых файлов: 645 добавлений и 186 удалений

Просмотреть файл

@ -425,8 +425,9 @@ if(BUILD_TESTS)
# Merkle Tree memory test
add_executable(merkle_mem src/node/test/merkle_mem.cpp)
target_link_libraries(
merkle_mem PRIVATE ccfcrypto.host secp256k1.host
${CMAKE_THREAD_LIBS_INIT} $<BUILD_INTERFACE:merklecpp>
merkle_mem
PRIVATE ccfcrypto.host secp256k1.host ${CMAKE_THREAD_LIBS_INIT}
$<BUILD_INTERFACE:merklecpp> crypto
)
use_client_mbedtls(merkle_mem)
@ -474,12 +475,12 @@ if(BUILD_TESTS)
add_picobench(
merkle_bench
SRCS src/node/test/merkle_bench.cpp
LINK_LIBS ccfcrypto.host secp256k1.host
LINK_LIBS ccfcrypto.host secp256k1.host crypto
)
add_picobench(
history_bench
SRCS src/node/test/history_bench.cpp
LINK_LIBS ccfcrypto.host secp256k1.host
LINK_LIBS ccfcrypto.host secp256k1.host crypto
)
add_picobench(
kv_bench

Просмотреть файл

@ -46,6 +46,7 @@ if(LVI_MITIGATIONS)
set(OE_TARGET_ENCLAVE_AND_STD
openenclave::oeenclave-lvi-cfg openenclave::oecryptombedtls-lvi-cfg
openenclave::oelibcxx-lvi-cfg openenclave::oelibc-lvi-cfg
openenclave::oecryptoopenssl-lvi-cfg
)
set(OE_TARGET_ENCLAVE_CORE_LIBS
openenclave::oeenclave-lvi-cfg openenclave::oecryptombedtls-lvi-cfg

Просмотреть файл

@ -7,15 +7,6 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
find_package(Threads REQUIRED)
# merklecpp traces are disabled by default to avoid unnecessary clutter.
set(TRACE
OFF
CACHE BOOL "Enable merklecpp traces"
)
set(USE_CCF_LOG
ON
CACHE BOOL "Use the CCF logging infrastructure for traces"
)
add_subdirectory(${CCF_DIR}/src/libmerklecpp)
set(PYTHON unbuffer python3)
@ -205,7 +196,7 @@ function(add_unit_test name)
enable_coverage(${name})
target_link_libraries(
${name} PRIVATE ${LINK_LIBCXX} ccfcrypto.host openenclave::oehostverify
$<BUILD_INTERFACE:merklecpp>
$<BUILD_INTERFACE:merklecpp> crypto
)
use_client_mbedtls(${name})
add_san(${name})

Просмотреть файл

@ -7,7 +7,7 @@ add_picobench(
SRCS ${CMAKE_CURRENT_LIST_DIR}/tests/small_bank_serdes_bench.cpp
src/crypto/symmetric_key.cpp src/enclave/thread_local.cpp
INCLUDE_DIRS ${CMAKE_CURRENT_LIST_DIR}
LINK_LIBS ccfcrypto.host secp256k1.host
LINK_LIBS ccfcrypto.host secp256k1.host crypto
)
add_client_exe(

Просмотреть файл

@ -6,55 +6,61 @@ set(CMAKE_CXX_STANDARD 17)
set(MERKLECPP_DIR ${CMAKE_CURRENT_SOURCE_DIR})
option(PROFILE "enable profiling" OFF)
option(EVERCRYPT "enable comparison with EverCrypt Merkle trees" OFF)
option(TESTS "enable testing" OFF)
option(EVERCRYPT "enable comparison with EverCrypt Merkle trees" OFF)
option(OPENSSL "enable OpenSSL" OFF)
option(MBEDTLS "enable mbedTLS" OFF)
option(TRACE "enable debug traces" OFF)
option(USE_CCF_LOG "Use the CCF logging infrastructure for traces" OFF)
add_library(merklecpp INTERFACE)
target_include_directories(merklecpp INTERFACE .)
if(TRACE)
target_compile_definitions(merklecpp INTERFACE WITH_TRACE)
endif()
if(USE_CCF_LOG)
target_compile_definitions(merklecpp INTERFACE USE_CCF_LOG)
target_compile_definitions(merklecpp INTERFACE MERKLECPP_TRACE_ENABLED)
endif()
install(TARGETS merklecpp)
if(TESTS)
if(EVERCRYPT)
if(NOT (DEFINED $ENV{EVERCRYPT_DIR}))
set(EVERCRYPT_DIR ../../3rdparty/hacl-star/evercrypt)
endif()
set(CMAKE_FIND_LIBRARY_SUFFIXES ".a" ".so")
file(GLOB EVERCRYPT_SRC "${EVERCRYPT_DIR}/*.c"
"${EVERCRYPT_DIR}/*-x86_64-linux.S"
)
add_library(evercrypt.host STATIC ${EVERCRYPT_SRC})
target_include_directories(
evercrypt.host PUBLIC ${EVERCRYPT_DIR} ${EVERCRYPT_DIR}/kremlin
${EVERCRYPT_DIR}/kremlin/kremlib
)
set_target_properties(evercrypt.host PROPERTIES LINKER_LANGUAGE C)
if(EVERCRYPT)
if(NOT EVERCRYPT_DIR)
message(FATAL_ERROR "EverCrypt not found, add -DEVERCRYPT_DIR=...")
endif()
file(GLOB EVERCRYPT_SRC "${EVERCRYPT_DIR}/*.c"
"${EVERCRYPT_DIR}/*-x86_64-linux.S"
)
add_library(evercrypt STATIC ${EVERCRYPT_SRC})
target_include_directories(
evercrypt PUBLIC ${EVERCRYPT_DIR} ${EVERCRYPT_DIR}/kremlin
${EVERCRYPT_DIR}/kremlin/kremlib
)
set_target_properties(evercrypt PROPERTIES LINKER_LANGUAGE C)
target_compile_definitions(merklecpp INTERFACE HAVE_EVERCRYPT)
target_link_libraries(merklecpp INTERFACE evercrypt)
endif()
if(OPENSSL)
find_package(OpenSSL)
target_compile_definitions(merklecpp INTERFACE HAVE_OPENSSL)
target_link_libraries(merklecpp INTERFACE crypto)
endif()
if(MBEDTLS)
find_library(MBEDCRYPTO_LIBRARY NMAMES libmbedcrypto.a)
target_compile_definitions(merklecpp INTERFACE HAVE_MBEDTLS)
target_link_libraries(merklecpp INTERFACE mbedcrypto)
endif()
if(TESTS)
enable_testing()
function(add_unit_test NAME SRC)
add_executable(${NAME} ${SRC})
target_link_libraries(${NAME} PRIVATE $<BUILD_INTERFACE:merklecpp>)
if(TRACE)
target_compile_definitions(${NAME} PRIVATE WITH_TRACE)
if(USE_CCF_LOG)
target_compile_definitions(${NAME} PRIVATE USE_CCF_LOG)
endif()
endif()
if(PROFILE)
target_compile_options(${NAME} PRIVATE -g -pg)
target_link_options(${NAME} PRIVATE -g -pg)
@ -67,11 +73,6 @@ if(TESTS)
target_link_options(${NAME} PRIVATE -fsanitize=address)
endif()
if(EVERCRYPT)
target_link_libraries(${NAME} PRIVATE evercrypt.host)
target_compile_definitions(${NAME} PRIVATE HAVE_EVERCRYPT)
endif()
add_test(${NAME} ${NAME})
endfunction()

Просмотреть файл

@ -32,30 +32,29 @@
# undef max
#endif
// Hashes in the trace output are truncated to TRACE_HASH_SIZE bytes.
#define TRACE_HASH_SIZE 3
#ifdef HAVE_OPENSSL
# include <openssl/sha.h>
#endif
#ifdef WITH_TRACE
# ifdef USE_CCF_LOG
// Use the CCF logging infrastructure to enable tracing in enclaves.
# include "ds/logger.h"
# define TRACE(X) \
{ \
X; \
};
# define TOUT LOG_TRACE
# else
// Send trace output to std::cout.
#ifdef HAVE_MBEDTLS
# include <mbedtls/sha256.h>
#endif
#ifdef MERKLECPP_TRACE_ENABLED
// Hashes in the trace output are truncated to TRACE_HASH_SIZE bytes.
# define TRACE_HASH_SIZE 3
# ifndef MERKLECPP_TRACE
# include <iostream>
# define TOUT std::cout
# define TRACE(X) \
# define MERKLECPP_TOUT std::cout
# define MERKLECPP_TRACE(X) \
{ \
X; \
TOUT.flush(); \
MERKLECPP_TOUT.flush(); \
};
# endif
#else
# define TRACE(X)
# define MERKLECPP_TRACE(X)
#endif
namespace merkle
@ -160,14 +159,14 @@ namespace merkle
void serialise(std::vector<uint8_t>& buffer) const
{
TRACE(TOUT << "> HashT::serialise " << std::endl);
MERKLECPP_TRACE(MERKLECPP_TOUT << "> HashT::serialise " << std::endl);
for (auto& b : bytes)
buffer.push_back(b);
}
void deserialise(const std::vector<uint8_t>& buffer, size_t& position)
{
TRACE(TOUT << "> HashT::deserialise " << std::endl);
MERKLECPP_TRACE(MERKLECPP_TOUT << "> HashT::deserialise " << std::endl);
if (buffer.size() - position < SIZE)
throw std::runtime_error("not enough bytes");
for (size_t i = 0; i < sizeof(bytes); i++)
@ -241,34 +240,38 @@ namespace merkle
bool verify(const HashT<HASH_SIZE>& root) const
{
HashT<HASH_SIZE> result = _leaf, tmp;
TRACE(
TOUT << "> PathT::verify " << _leaf.to_string(TRACE_HASH_SIZE)
<< std::endl);
MERKLECPP_TRACE(
MERKLECPP_TOUT << "> PathT::verify " << _leaf.to_string(TRACE_HASH_SIZE)
<< std::endl);
for (const Element& e : elements)
{
if (e.direction == PATH_LEFT)
{
TRACE(
TOUT << " - " << e.hash.to_string(TRACE_HASH_SIZE) << " x "
<< result.to_string(TRACE_HASH_SIZE) << std::endl);
MERKLECPP_TRACE(
MERKLECPP_TOUT << " - " << e.hash.to_string(TRACE_HASH_SIZE)
<< " x " << result.to_string(TRACE_HASH_SIZE)
<< std::endl);
HASH_FUNCTION(e.hash, result, tmp);
}
else
{
TRACE(
TOUT << " - " << result.to_string(TRACE_HASH_SIZE) << " x "
<< e.hash.to_string(TRACE_HASH_SIZE) << std::endl);
MERKLECPP_TRACE(
MERKLECPP_TOUT << " - " << result.to_string(TRACE_HASH_SIZE)
<< " x " << e.hash.to_string(TRACE_HASH_SIZE)
<< std::endl);
HASH_FUNCTION(result, e.hash, tmp);
}
std::swap(result, tmp);
}
TRACE(TOUT << " = " << result.to_string(TRACE_HASH_SIZE) << std::endl);
MERKLECPP_TRACE(
MERKLECPP_TOUT << " = " << result.to_string(TRACE_HASH_SIZE)
<< std::endl);
return result == root;
}
void serialise(std::vector<uint8_t>& bytes) const
{
TRACE(TOUT << "> PathT::serialise " << std::endl);
MERKLECPP_TRACE(MERKLECPP_TOUT << "> PathT::serialise " << std::endl);
_leaf.serialise(bytes);
serialise_size_t(_leaf_index, bytes);
serialise_size_t(_max_index, bytes);
@ -282,7 +285,7 @@ namespace merkle
void deserialise(const std::vector<uint8_t>& bytes, size_t& position)
{
TRACE(TOUT << "> PathT::deserialise " << std::endl);
MERKLECPP_TRACE(MERKLECPP_TOUT << "> PathT::deserialise " << std::endl);
elements.clear();
_leaf.deserialise(bytes, position);
_leaf_index = deserialise_size_t(bytes, position);
@ -540,8 +543,9 @@ namespace merkle
void insert(const Hash& hash)
{
TRACE(TOUT << "> insert " << hash.to_string(TRACE_HASH_SIZE)
<< std::endl;);
MERKLECPP_TRACE(MERKLECPP_TOUT << "> insert "
<< hash.to_string(TRACE_HASH_SIZE)
<< std::endl;);
uninserted_leaf_nodes.push_back(Node::make(hash));
statistics.num_insert++;
}
@ -560,14 +564,16 @@ namespace merkle
void flush_to(size_t index)
{
TRACE(TOUT << "> flush_to " << index << std::endl;);
MERKLECPP_TRACE(MERKLECPP_TOUT << "> flush_to " << index << std::endl;);
statistics.num_flush++;
walk_to(index, false, [this](Node*& n, bool go_right) {
if (go_right && n->left)
{
TRACE(TOUT << " - conflate "
<< n->left->hash.to_string(TRACE_HASH_SIZE) << std::endl;);
MERKLECPP_TRACE(MERKLECPP_TOUT
<< " - conflate "
<< n->left->hash.to_string(TRACE_HASH_SIZE)
<< std::endl;);
if (n->left && n->left->dirty)
hash(n->left);
delete (n->left->left);
@ -586,7 +592,7 @@ namespace merkle
void retract_to(size_t index)
{
TRACE(TOUT << "> retract_to " << index << std::endl;);
MERKLECPP_TRACE(MERKLECPP_TOUT << "> retract_to " << index << std::endl;);
statistics.num_retract++;
if (max_index() < index)
@ -611,9 +617,10 @@ namespace merkle
n->dirty = true;
if (go_left && n->right)
{
TRACE(TOUT << " - eliminate "
<< n->right->hash.to_string(TRACE_HASH_SIZE)
<< std::endl;);
MERKLECPP_TRACE(MERKLECPP_TOUT
<< " - eliminate "
<< n->right->hash.to_string(TRACE_HASH_SIZE)
<< std::endl;);
bool is_root = n == _root;
Node* old_parent = n->parent;
@ -638,16 +645,18 @@ namespace merkle
if (is_root)
{
TRACE(TOUT << " - new root: " << n->hash.to_string(TRACE_HASH_SIZE)
<< std::endl;);
MERKLECPP_TRACE(MERKLECPP_TOUT << " - new root: "
<< n->hash.to_string(TRACE_HASH_SIZE)
<< std::endl;);
assert(n->parent == nullptr);
assert(_root == n);
}
assert(n->invariant());
TRACE(TOUT << " - after elimination: " << std::endl
<< to_string(TRACE_HASH_SIZE) << std::endl;);
MERKLECPP_TRACE(MERKLECPP_TOUT
<< " - after elimination: " << std::endl
<< to_string(TRACE_HASH_SIZE) << std::endl;);
return false;
}
else
@ -683,7 +692,8 @@ namespace merkle
const Tree split(size_t index)
{
TRACE(TOUT << "> split (slow) at " << index << std::endl;);
MERKLECPP_TRACE(MERKLECPP_TOUT << "> split (slow) at " << index
<< std::endl;);
Tree other = *this;
if (index > num_flushed)
other.retract_to(index - 1);
@ -693,24 +703,27 @@ namespace merkle
const Hash& root()
{
TRACE(TOUT << "> root" << std::endl;);
MERKLECPP_TRACE(MERKLECPP_TOUT << "> root" << std::endl;);
statistics.num_root++;
compute_root();
assert(_root && !_root->dirty);
TRACE(TOUT << " - root: " << _root->hash.to_string(TRACE_HASH_SIZE)
<< std::endl;);
MERKLECPP_TRACE(MERKLECPP_TOUT
<< " - root: " << _root->hash.to_string(TRACE_HASH_SIZE)
<< std::endl;);
return _root->hash;
}
std::shared_ptr<Hash> past_root(size_t index)
{
TRACE(TOUT << "> past_root " << index << std::endl;);
MERKLECPP_TRACE(MERKLECPP_TOUT << "> past_root " << index << std::endl;);
statistics.num_past_root++;
auto p = path(index);
auto result = std::make_shared<Hash>(p->leaf());
TRACE(TOUT << " - " << p->to_string(TRACE_HASH_SIZE) << std::endl;
TOUT << " - " << result->to_string(TRACE_HASH_SIZE) << std::endl;);
MERKLECPP_TRACE(
MERKLECPP_TOUT << " - " << p->to_string(TRACE_HASH_SIZE) << std::endl;
MERKLECPP_TOUT << " - " << result->to_string(TRACE_HASH_SIZE)
<< std::endl;);
for (auto e : *p)
{
if (e.direction == Path::Direction::PATH_LEFT)
@ -746,9 +759,12 @@ namespace merkle
bool go_right = (it >> (8 * sizeof(it) - 1)) & 0x01;
if (update)
walk_stack.push_back(cur);
TRACE(TOUT << " - at " << cur->hash.to_string(TRACE_HASH_SIZE) << " ("
<< cur->size << "/" << (unsigned)cur->height << ")"
<< " (" << (go_right ? "R" : "L") << ")" << std::endl;);
MERKLECPP_TRACE(MERKLECPP_TOUT
<< " - at " << cur->hash.to_string(TRACE_HASH_SIZE)
<< " (" << cur->size << "/" << (unsigned)cur->height
<< ")"
<< " (" << (go_right ? "R" : "L") << ")"
<< std::endl;);
if (cur->height == height)
{
if (!f(cur, go_right))
@ -770,7 +786,7 @@ namespace merkle
std::unique_ptr<Path> path(size_t index)
{
TRACE(TOUT << "> path from " << index << std::endl;);
MERKLECPP_TRACE(MERKLECPP_TOUT << "> path from " << index << std::endl;);
std::list<typename Path::Element> elements;
walk_to(index, false, [&elements](Node* n, bool go_right) {
@ -787,11 +803,12 @@ namespace merkle
void serialise(std::vector<uint8_t>& bytes)
{
TRACE(TOUT << "> serialise " << std::endl;);
MERKLECPP_TRACE(MERKLECPP_TOUT << "> serialise " << std::endl;);
compute_root();
TRACE(TOUT << to_string(TRACE_HASH_SIZE) << std::endl;);
MERKLECPP_TRACE(MERKLECPP_TOUT << to_string(TRACE_HASH_SIZE)
<< std::endl;);
serialise_size_t(leaf_nodes.size() + uninserted_leaf_nodes.size(), bytes);
serialise_size_t(num_flushed, bytes);
@ -819,7 +836,7 @@ namespace merkle
void deserialise(const std::vector<uint8_t>& bytes, size_t& position)
{
TRACE(TOUT << "> deserialise " << std::endl;);
MERKLECPP_TRACE(MERKLECPP_TOUT << "> deserialise " << std::endl;);
delete (_root);
leaf_nodes.clear();
@ -844,8 +861,8 @@ namespace merkle
std::vector<Node*> level = leaf_nodes, next_level;
size_t it = num_flushed;
TRACE(TOUT << "num_flushed=" << num_flushed << " it=" << it
<< std::endl;);
MERKLECPP_TRACE(MERKLECPP_TOUT << "num_flushed=" << num_flushed
<< " it=" << it << std::endl;);
uint8_t level_no = 0;
while (it != 0 || level.size() > 1)
{
@ -853,7 +870,7 @@ namespace merkle
if (it & 0x01)
{
Hash h(bytes, position);
TRACE(TOUT << "+";);
MERKLECPP_TRACE(MERKLECPP_TOUT << "+";);
auto n = Node::make(h);
n->height = level_no + 1;
n->size = (1 << n->height) - 1;
@ -861,10 +878,10 @@ namespace merkle
level.insert(level.begin(), n);
}
TRACE(for (auto& n
: level) TOUT
<< " " << n->hash.to_string(TRACE_HASH_SIZE);
TOUT << std::endl;);
MERKLECPP_TRACE(for (auto& n
: level) MERKLECPP_TOUT
<< " " << n->hash.to_string(TRACE_HASH_SIZE);
MERKLECPP_TOUT << std::endl;);
// Rebuild the level
for (size_t i = 0; i < level.size(); i += 2)
@ -905,7 +922,7 @@ namespace merkle
const Hash& leaf(size_t index) const
{
TRACE(TOUT << "> leaf " << index << std::endl;);
MERKLECPP_TRACE(MERKLECPP_TOUT << "> leaf " << index << std::endl;);
if (index >= num_leaves())
throw std::runtime_error("leaf index out of bounds");
if (index - num_flushed >= leaf_nodes.size())
@ -1043,7 +1060,7 @@ namespace merkle
void hash(Node* n, size_t indent = 2) const
{
#ifndef WITH_TRACE
#ifndef MERKLECPP_WITH_TRACE
(void)indent;
#endif
@ -1065,12 +1082,13 @@ namespace merkle
assert(n->left && n->right);
HASH_FUNCTION(n->left->hash, n->right->hash, n->hash);
statistics.num_hash++;
TRACE(
TOUT << std::string(indent, ' ') << "+ h("
<< n->left->hash.to_string(TRACE_HASH_SIZE) << ", "
<< n->right->hash.to_string(TRACE_HASH_SIZE)
<< ") == " << n->hash.to_string(TRACE_HASH_SIZE) << " ("
<< n->size << "/" << (unsigned)n->height << ")" << std::endl);
MERKLECPP_TRACE(
MERKLECPP_TOUT << std::string(indent, ' ') << "+ h("
<< n->left->hash.to_string(TRACE_HASH_SIZE) << ", "
<< n->right->hash.to_string(TRACE_HASH_SIZE)
<< ") == " << n->hash.to_string(TRACE_HASH_SIZE)
<< " (" << n->size << "/" << (unsigned)n->height
<< ")" << std::endl);
n->dirty = false;
hashing_stack.pop_back();
}
@ -1106,8 +1124,9 @@ namespace merkle
}
else
{
TRACE(TOUT << " @ " << n->hash.to_string(TRACE_HASH_SIZE)
<< std::endl;);
MERKLECPP_TRACE(MERKLECPP_TOUT << " @ "
<< n->hash.to_string(TRACE_HASH_SIZE)
<< std::endl;);
assert(n->left && n->right);
if (!n->left->is_full())
insert_leaf_recursive(n->left, new_leaf);
@ -1123,8 +1142,9 @@ namespace merkle
{
while (true)
{
TRACE(TOUT << " @ " << n->hash.to_string(TRACE_HASH_SIZE)
<< std::endl;);
MERKLECPP_TRACE(MERKLECPP_TOUT << " @ "
<< n->hash.to_string(TRACE_HASH_SIZE)
<< std::endl;);
assert(n->invariant());
if (n->is_full())
@ -1160,10 +1180,11 @@ namespace merkle
Node* process_insertion_stack(bool complete = true)
{
TRACE(TOUT << " X " << (complete ? "complete" : "continue") << ":";
for (size_t i = 0; i < insertion_stack.size(); i++) TOUT
<< " " << insertion_stack[i].n->hash.to_string(TRACE_HASH_SIZE);
TOUT << std::endl;);
MERKLECPP_TRACE(
MERKLECPP_TOUT << " X " << (complete ? "complete" : "continue") << ":";
for (size_t i = 0; i < insertion_stack.size(); i++) MERKLECPP_TOUT
<< " " << insertion_stack[i].n->hash.to_string(TRACE_HASH_SIZE);
MERKLECPP_TOUT << std::endl;);
Node* result = insertion_stack.back().n;
insertion_stack.pop_back();
@ -1189,8 +1210,10 @@ namespace merkle
if (!complete && !result->is_full())
{
TRACE(TOUT << " X save " << result->hash.to_string(TRACE_HASH_SIZE)
<< std::endl;);
MERKLECPP_TRACE(MERKLECPP_TOUT
<< " X save "
<< result->hash.to_string(TRACE_HASH_SIZE)
<< std::endl;);
return result;
}
}
@ -1202,8 +1225,9 @@ namespace merkle
void insert_leaf(Node*& root, Node* n)
{
TRACE(TOUT << " - insert_leaf " << n->hash.to_string(TRACE_HASH_SIZE)
<< std::endl;);
MERKLECPP_TRACE(MERKLECPP_TOUT << " - insert_leaf "
<< n->hash.to_string(TRACE_HASH_SIZE)
<< std::endl;);
leaf_nodes.push_back(n);
if (insertion_stack.empty() && !root)
root = n;
@ -1218,8 +1242,9 @@ namespace merkle
{
if (!uninserted_leaf_nodes.empty())
{
TRACE(TOUT << "* insert_leaves " << leaf_nodes.size() << " +"
<< uninserted_leaf_nodes.size() << std::endl;);
MERKLECPP_TRACE(MERKLECPP_TOUT
<< "* insert_leaves " << leaf_nodes.size() << " +"
<< uninserted_leaf_nodes.size() << std::endl;);
// Potential future improvement: make this go fast when there are many
// leaves to insert.
for (auto& n : uninserted_leaf_nodes)
@ -1232,7 +1257,7 @@ namespace merkle
};
// clang-format off
static void sha256_compress(const HashT<32> &l, const HashT<32> &r, HashT<32> &out) {
void sha256_compress(const HashT<32> &l, const HashT<32> &r, HashT<32> &out) {
static const uint32_t constants[] = {
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
@ -1291,14 +1316,13 @@ namespace merkle
// clang-format on
#ifdef HAVE_OPENSSL
# include <openssl/sha.h>
// Note: Some versions of OpenSSL don't provide SHA256_Transform.
void sha256_compress_openssl(
const uint8_t* h1, const uint8_t* h2, uint8_t* out)
inline void sha256_compress_openssl(
const HashT<32>& l, const HashT<32>& r, HashT<32>& out)
{
unsigned char block[HASH_SIZE * 2];
memcpy(&block[0], h1, HASH_SIZE);
memcpy(&block[HASH_SIZE], h2, HASH_SIZE);
unsigned char block[32 * 2];
memcpy(&block[0], l.bytes, 32);
memcpy(&block[32], r.bytes, 32);
SHA256_CTX ctx;
if (SHA256_Init(&ctx) != 1)
@ -1306,7 +1330,49 @@ namespace merkle
SHA256_Transform(&ctx, &block[0]);
for (int i = 0; i < 8; i++)
((uint32_t*)out)[i] = htobe32(((uint32_t*)ctx.h)[i]);
((uint32_t*)out.bytes)[i] = htobe32(((uint32_t*)ctx.h)[i]);
}
inline void sha256_openssl(
const merkle::HashT<32>& l,
const merkle::HashT<32>& r,
merkle::HashT<32>& out)
{
uint8_t block[32 * 2];
memcpy(&block[0], l.bytes, 32);
memcpy(&block[32], r.bytes, 32);
SHA256(block, sizeof(block), out.bytes);
}
#endif
#ifdef HAVE_MBEDTLS
// Note: Technically, mbedtls_internal_sha256_process is for internal use
// only.
inline void sha256_compress_mbedtls(
const HashT<32>& l, const HashT<32>& r, HashT<32>& out)
{
unsigned char block[32 * 2];
memcpy(&block[0], l.bytes, 32);
memcpy(&block[32], r.bytes, 32);
mbedtls_sha256_context ctx;
mbedtls_sha256_init(&ctx);
mbedtls_sha256_starts_ret(&ctx, false);
mbedtls_internal_sha256_process(&ctx, &block[0]);
for (int i = 0; i < 8; i++)
((uint32_t*)out.bytes)[i] = htobe32(ctx.state[i]);
}
inline void sha256_mbedtls(
const merkle::HashT<32>& l,
const merkle::HashT<32>& r,
merkle::HashT<32>& out)
{
uint8_t block[32 * 2];
memcpy(&block[0], l.bytes, 32);
memcpy(&block[32], r.bytes, 32);
mbedtls_sha256_ret(block, sizeof(block), out.bytes, false);
}
#endif
@ -1314,4 +1380,4 @@ namespace merkle
typedef HashT<32> Hash;
typedef PathT<32, sha256_compress> Path;
typedef TreeT<32, sha256_compress> Tree;
};
};

Просмотреть файл

@ -11,3 +11,12 @@ add_unit_test(${MERKLECPP_TEST_PREFIX}serialisation serialisation.cpp)
if(TARGET evercrypt.host)
add_unit_test(${MERKLECPP_TEST_PREFIX}compare_evercrypt compare_evercrypt.cpp)
endif()
if(OPENSSL
OR MBEDTLS
OR EVERCRYPT
)
add_unit_test(
${MERKLECPP_TEST_PREFIX}compare_hash_functions compare_hash_functions.cpp
)
endif()

Просмотреть файл

@ -0,0 +1,396 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the Apache 2.0 License.
#include "util.h"
#include <chrono>
#include <iomanip>
#include <iostream>
#include <merklecpp.h>
#define HSZ 32
#define PRNTSZ 3
#ifdef HAVE_EVERCRYPT
# include <Hacl_Hash.h>
# include <MerkleTree.h>
void sha256_compress_evercrypt(
const merkle::HashT<32>& l,
const merkle::HashT<32>& r,
merkle::HashT<32>& out)
{
mt_sha256_compress((uint8_t*)l.bytes, (uint8_t*)r.bytes, (uint8_t*)out.bytes);
}
void sha256_evercrypt(
const merkle::HashT<32>& l,
const merkle::HashT<32>& r,
merkle::HashT<32>& out)
{
uint8_t block[32 * 2];
memcpy(&block[0], l.bytes, 32);
memcpy(&block[32], r.bytes, 32);
Hacl_Hash_SHA2_hash_256(block, sizeof(block), (uint8_t*)out.bytes);
}
void mt_sha256_evercrypt(uint8_t* src1, uint8_t* src2, uint8_t* dst)
{
uint8_t block[32 * 2];
memcpy(&block[0], src1, 32);
memcpy(&block[32], src2, 32);
Hacl_Hash_SHA2_hash_256(block, sizeof(block), dst);
}
typedef merkle::TreeT<32, sha256_compress_evercrypt> EverCryptTree;
typedef merkle::TreeT<32, sha256_evercrypt> EverCryptFullTree;
#endif
#ifdef HAVE_OPENSSL
typedef merkle::TreeT<32, merkle::sha256_compress_openssl> OpenSSLTree;
typedef merkle::TreeT<32, merkle::sha256_openssl> OpenSSLFullTree;
#endif
#ifdef HAVE_MBEDTLS
typedef merkle::TreeT<32, merkle::sha256_compress_mbedtls> MbedTLSTree;
typedef merkle::TreeT<32, merkle::sha256_mbedtls> MbedTLSFullTree;
#endif
template <
void (*HF1)(
const merkle::HashT<32>& l,
const merkle::HashT<32>& r,
merkle::HashT<32>& out),
void (*HF2)(
const merkle::HashT<32>& l,
const merkle::HashT<32>& r,
merkle::HashT<32>& out)>
void compare_roots(
merkle::TreeT<32, HF1>& mt1, merkle::TreeT<32, HF2>& mt2, const char* name)
{
auto mt1_root = mt1.root();
auto mt2_root = mt2.root();
if (mt1_root != mt2_root)
{
std::cout << mt1.num_leaves() << ": " << mt1_root.to_string()
<< " != " << mt2_root.to_string() << std::endl;
std::cout << "mt1: " << std::endl;
std::cout << mt1.to_string(PRNTSZ) << std::endl;
std::cout << name << ": " << std::endl;
std::cout << mt2.to_string(PRNTSZ) << std::endl;
throw std::runtime_error("root hash mismatch");
}
}
void compare_compression_hashes()
{
#ifndef NDEBUG
const size_t num_trees = 1024;
const size_t root_interval = 31;
#else
const size_t num_trees = 4096;
const size_t root_interval = 128;
#endif
size_t total_inserts = 0, total_roots = 0;
for (size_t k = 0; k < num_trees; k++)
{
merkle::Tree mt;
#ifdef HAVE_EVERCRYPT
EverCryptTree mte;
#endif
#ifdef HAVE_OPENSSL
OpenSSLTree mto;
#endif
#ifdef HAVE_MBEDTLS
MbedTLSTree mtm;
#endif
// Build trees with k+1 leaves
int j = 0;
auto hashes = make_hashes(k + 1);
for (const auto h : hashes)
{
mt.insert(h);
#ifdef HAVE_EVERCRYPT
mte.insert(h);
#endif
#ifdef HAVE_OPENSSL
mto.insert(h);
#endif
#ifdef HAVE_MBEDTLS
mtm.insert(h);
#endif
total_inserts++;
if ((j++ % root_interval) == 0)
{
#ifdef HAVE_EVERCRYPT
compare_roots(mt, mte, "EverCrypt");
#endif
#ifdef HAVE_OPENSSL
compare_roots(mt, mto, "OpenSSL");
#endif
#ifdef HAVE_MBEDTLS
compare_roots(mt, mtm, "mbedTLS");
#endif
total_roots++;
}
}
#ifdef HAVE_EVERCRYPT
compare_roots(mt, mte, "EverCrypt");
#endif
#ifdef HAVE_OPENSSL
compare_roots(mt, mto, "OpenSSL");
#endif
#ifdef HAVE_MBEDTLS
compare_roots(mt, mtm, "mbedTLS");
#endif
}
static char time_str[256] = "";
std::time_t t = std::time(nullptr);
std::strftime(time_str, sizeof(time_str), "%R", std::localtime(&t));
std::cout << time_str << ": " << num_trees << " trees, " << total_inserts
<< " inserts, " << total_roots
<< " roots with SHA256 compression function: OK" << std::endl;
}
#if defined(HAVE_OPENSSL) && (defined(HAVE_EVERCRYPT) || defined(HAVE_MBEDTLS))
void compare_full_hashes()
{
# ifndef NDEBUG
const size_t num_trees = 1024;
const size_t root_interval = 31;
# else
const size_t num_trees = 4096;
const size_t root_interval = 128;
# endif
size_t total_inserts = 0, total_roots = 0;
for (size_t k = 0; k < num_trees; k++)
{
OpenSSLFullTree mto;
# ifdef HAVE_EVERCRYPT
merkle::TreeT<32, sha256_evercrypt> mte;
# endif
# ifdef HAVE_MBEDTLS
MbedTLSFullTree mtm;
# endif
// Build trees with k+1 leaves
int j = 0;
auto hashes = make_hashes(k + 1);
for (const auto h : hashes)
{
mto.insert(h);
# ifdef HAVE_EVERCRYPT
mte.insert(h);
# endif
# ifdef HAVE_MBEDTLS
mtm.insert(h);
# endif
total_inserts++;
if ((j++ % root_interval) == 0)
{
# ifdef HAVE_EVERCRYPT
compare_roots(mto, mte, "EverCrypt");
# endif
# ifdef HAVE_MBEDTLS
compare_roots(mto, mtm, "mbedTLS");
# endif
total_roots++;
}
}
# ifdef HAVE_EVERCRYPT
compare_roots(mto, mte, "OpenSSL");
# endif
# ifdef HAVE_MBEDTLS
compare_roots(mto, mtm, "mbedTLS");
# endif
}
static char time_str[256] = "";
std::time_t t = std::time(nullptr);
std::strftime(time_str, sizeof(time_str), "%R", std::localtime(&t));
std::cout << time_str << ": " << num_trees << " trees, " << total_inserts
<< " inserts, " << total_roots << " roots with full SHA256: OK"
<< std::endl;
}
#endif
template <typename T>
void bench(
const std::vector<merkle::Hash>& hashes,
const std::string& name,
size_t root_interval)
{
size_t j = 0;
auto start = std::chrono::high_resolution_clock::now();
T mt;
for (auto& h : hashes)
{
mt.insert(h);
if ((j++ % root_interval) == 0)
mt.root();
}
mt.root();
auto stop = std::chrono::high_resolution_clock::now();
double seconds =
std::chrono::duration_cast<std::chrono::nanoseconds>(stop - start).count() /
1e9;
std::cout << std::left << std::setw(10) << name << ": "
<< mt.statistics.num_insert << " insertions, "
<< mt.statistics.num_root << " roots in " << seconds << " sec"
<< std::endl;
}
#ifdef HAVE_EVERCRYPT
template <void (*HASH_FUNCTION)(uint8_t* l, uint8_t* r, uint8_t* out)>
void bench_evercrypt(
const std::vector<uint8_t*>& hashes,
const std::string& name,
size_t root_interval)
{
size_t j = 0, num_inserts = 0, num_roots = 0;
uint8_t* ec_root = mt_init_hash(32);
auto start = std::chrono::high_resolution_clock::now();
merkle_tree* ec_mt = mt_create_custom(32, hashes[0], HASH_FUNCTION);
for (size_t i = 1; i < hashes.size(); i++)
{
mt_insert(ec_mt, hashes[i]);
num_inserts++;
if ((j++ % root_interval) == 0)
{
mt_get_root(ec_mt, ec_root);
num_roots++;
}
}
mt_get_root(ec_mt, ec_root);
num_roots++;
auto stop = std::chrono::high_resolution_clock::now();
auto seconds =
std::chrono::duration_cast<std::chrono::nanoseconds>(stop - start).count() /
1e9;
std::cout << std::left << std::setw(10) << name << ": " << num_inserts
<< " insertions, " << num_roots << " roots in " << seconds << " sec"
<< std::endl;
mt_free_hash(ec_root);
mt_free(ec_mt);
}
#endif
int main()
{
try
{
// std::srand(0);
std::srand(std::time(0));
compare_compression_hashes();
#if defined(HAVE_EVERCRYPT) && (defined(HAVE_OPENSSL) || defined(HAVE_MBEDTLS))
compare_full_hashes();
#endif
#ifndef NDEBUG
const size_t num_leaves = 128 * 1024;
const size_t root_interval = 128;
#else
const size_t num_leaves = 16 * 1024 * 1024;
const size_t root_interval = 1024;
#endif
auto hashes = make_hashes(num_leaves);
std::cout << "--- merklecpp trees with SHA256 compression function: "
<< std::endl;
bench<merkle::Tree>(hashes, "merklecpp", root_interval);
#ifdef HAVE_OPENSSL
bench<OpenSSLTree>(hashes, "OpenSSL", root_interval);
#endif
#ifdef HAVE_MBEDTLS
bench<MbedTLSTree>(hashes, "mbedTLS", root_interval);
#endif
#ifdef HAVE_EVERCRYPT
bench<EverCryptTree>(hashes, "EverCrypt", root_interval);
#endif
std::cout << "--- merklecpp trees with full SHA256: " << std::endl;
#ifdef HAVE_OPENSSL
bench<OpenSSLFullTree>(hashes, "OpenSSL", root_interval);
#endif
#ifdef HAVE_MBEDTLS
bench<MbedTLSFullTree>(hashes, "mbedTLS", root_interval);
#endif
#ifdef HAVE_EVERCRYPT
bench<EverCryptFullTree>(hashes, "EverCrypt", root_interval);
#endif
#ifdef HAVE_EVERCRYPT
std::vector<uint8_t*> ec_hashes;
for (auto& h : hashes)
{
ec_hashes.push_back(mt_init_hash(32));
memcpy(ec_hashes.back(), h.bytes, 32);
}
std::cout << "--- EverCrypt trees with SHA256 compression function: "
<< std::endl;
bench_evercrypt<mt_sha256_compress>(ec_hashes, "EverCrypt", root_interval);
std::cout << "--- EverCrypt trees with full SHA256: " << std::endl;
bench_evercrypt<mt_sha256_evercrypt>(ec_hashes, "EverCrypt", root_interval);
for (auto h : ec_hashes)
mt_free_hash(h);
#endif
}
catch (std::exception& ex)
{
std::cout << "Error: " << ex.what() << std::endl;
return 1;
}
catch (...)
{
std::cout << "Error" << std::endl;
return 1;
}
return 0;
}

Просмотреть файл

@ -1,26 +0,0 @@
project(evercrypt LANGUAGES C ASM)
option(PROFILE "enable profiling" OFF)
option(EVERCRYPT "enable comparison with EverCrypt Merkle trees" OFF)
if(EVERCRYPT)
if(NOT (DEFINED $ENV{EVERCRYPT_DIR}))
set(EVERCRYPT_DIR ../../../../3rdparty/hacl-star/evercrypt)
endif()
set(EVERCRYPT_INC ${EVERCRYPT_DIR} ${EVERCRYPT_DIR}/kremlin
${EVERCRYPT_DIR}/kremlin/kremlib
)
file(GLOB EVEREST_SRC "${EVERCRYPT_DIR}/*.c"
"${EVERCRYPT_DIR}/*-x86_64-linux.S"
)
add_library(evercrypt STATIC ${EVEREST_SRC})
target_include_directories(evercrypt PRIVATE ${EVERCRYPT_INC})
if(PROFILE)
target_compile_options(evercrypt PRIVATE -g -pg)
target_link_options(evercrypt PRIVATE -g -pg)
endif()
endif()

Просмотреть файл

@ -31,13 +31,13 @@ int main()
merkle::Tree mt;
size_t j = 0;
auto start = std::chrono::high_resolution_clock::now();
for (auto h : hashes)
for (auto& h : hashes)
{
mt.insert(h);
if ((j++ % root_interval) == 0)
mt.root();
}
// auto root = mt.root();
auto root = mt.root();
auto stop = std::chrono::high_resolution_clock::now();
double seconds =
std::chrono::duration_cast<std::chrono::nanoseconds>(stop - start)
@ -48,7 +48,7 @@ int main()
#ifdef HAVE_EVERCRYPT
std::vector<uint8_t*> ec_hashes;
for (auto h : hashes)
for (auto& h : hashes)
{
ec_hashes.push_back(mt_init_hash(HSZ));
memcpy(ec_hashes.back(), h.bytes, HSZ);
@ -58,7 +58,6 @@ int main()
size_t num_ec_roots = 1;
start = std::chrono::high_resolution_clock::now();
merkle_tree* ec_mt = mt_create(ec_hashes[0]);
mt_get_root(ec_mt, ec_root);
for (size_t i = 1; i < ec_hashes.size(); i++)
{
mt_insert(ec_mt, ec_hashes[i]);
@ -68,6 +67,7 @@ int main()
num_ec_roots++;
}
}
mt_get_root(ec_mt, ec_root);
stop = std::chrono::high_resolution_clock::now();
seconds = std::chrono::duration_cast<std::chrono::nanoseconds>(stop - start)
.count() /

Просмотреть файл

@ -16,9 +16,14 @@
#include <array>
#include <deque>
#include <merklecpp.h>
#include <string.h>
#define HAVE_OPENSSL
#define HAVE_MBEDTLS
// merklecpp traces are off by default, even when CCF tracing is enabled
// #include "merklecpp_trace.h"
#include <merklecpp.h>
namespace fmt
{
template <>
@ -198,11 +203,13 @@ namespace ccf
}
};
typedef merkle::TreeT<32, merkle::sha256_openssl> HistoryTree;
class Receipt
{
private:
merkle::Hash root;
std::shared_ptr<merkle::Path> path = nullptr;
HistoryTree::Hash root;
std::shared_ptr<HistoryTree::Path> path = nullptr;
public:
Receipt() {}
@ -211,10 +218,10 @@ namespace ccf
{
size_t position = 0;
root.deserialise(v, position);
path = std::make_shared<merkle::Path>(v, position);
path = std::make_shared<HistoryTree::Path>(v, position);
}
Receipt(merkle::Tree* tree, uint64_t index)
Receipt(HistoryTree* tree, uint64_t index)
{
root = tree->root();
path = tree->path(index);
@ -222,7 +229,7 @@ namespace ccf
Receipt(const Receipt&) = delete;
bool verify(merkle::Tree* tree) const
bool verify(HistoryTree* tree) const
{
return tree->max_index() == path->max_index() && tree->root() == root &&
path->verify(root);
@ -239,19 +246,19 @@ namespace ccf
class MerkleTreeHistory
{
merkle::Tree* tree;
HistoryTree* tree;
public:
MerkleTreeHistory(MerkleTreeHistory const&) = delete;
MerkleTreeHistory(const std::vector<uint8_t>& serialised)
{
tree = new merkle::Tree(serialised);
tree = new HistoryTree(serialised);
}
MerkleTreeHistory(crypto::Sha256Hash first_hash = {})
{
tree = new merkle::Tree(merkle::Hash(first_hash.h));
tree = new HistoryTree(merkle::Hash(first_hash.h));
}
~MerkleTreeHistory()
@ -263,7 +270,7 @@ namespace ccf
void deserialise(const std::vector<uint8_t>& serialised)
{
delete (tree);
tree = new merkle::Tree(serialised);
tree = new HistoryTree(serialised);
}
void append(crypto::Sha256Hash& hash)
@ -283,7 +290,7 @@ namespace ccf
{
delete (tree);
crypto::Sha256Hash root(rhs.get_root());
tree = new merkle::Tree(merkle::Hash(root.h));
tree = new HistoryTree(merkle::Hash(root.h));
}
void flush(uint64_t index)

Просмотреть файл

@ -0,0 +1,13 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the Apache 2.0 License.
#pragma once
#include "ds/logger.h"
// Use the CCF logging infrastructure for merklecpp traces.
#define MERKLECPP_TRACE_ENABLED
#define MERKLECPP_TRACE(X) \
{ \
X; \
};
#define MERKLECPP_TOUT LOG_TRACE