Skip to content

Commit

Permalink
Faster SHA256 OpenSSL hashing (#5548)
Browse files Browse the repository at this point in the history
  • Loading branch information
jumaffre authored Aug 16, 2023
1 parent ac6f110 commit 6d9bcf6
Show file tree
Hide file tree
Showing 20 changed files with 170 additions and 24 deletions.
4 changes: 2 additions & 2 deletions .daily_canary
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
--- ___ ___
(- -) (= =) | Y & +--?
( V ) z . z O +---=---'
/--x-m- /--n-n---xXx--/--yY-----.....
( V ) \ . \ O +---=---'
/--x-m- /--n-n---xXx--/--yY-----.
2 changes: 1 addition & 1 deletion .threading_canary
Original file line number Diff line number Diff line change
@@ -1 +1 @@
THIS looks like a job for Threading Canar!y!1!..
THIS looks like a job for Threading Canard!y!1!..
3 changes: 3 additions & 0 deletions src/clients/perf/scenario_perf_client.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the Apache 2.0 License.
#include "crypto/openssl/hash.h"
#include "ds/files.h"
#include "perf_client.h"

Expand Down Expand Up @@ -132,6 +133,7 @@ int main(int argc, char** argv)
{
logger::config::default_init();
logger::config::level() = LoggerLevel::INFO;
crypto::openssl_sha256_init();

CLI::App cli_app{"Scenario Perf Client"};
ScenarioPerfClientOptions options(cli_app, argv[0]);
Expand All @@ -140,5 +142,6 @@ int main(int argc, char** argv)
ScenarioPerfClient client(options);
client.run();

crypto::openssl_sha256_shutdown();
return 0;
}
70 changes: 67 additions & 3 deletions src/crypto/openssl/hash.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,77 @@ namespace crypto

using namespace OpenSSL;

static thread_local EVP_MD_CTX* mdctx = nullptr;
static thread_local EVP_MD_CTX* basectx = nullptr;

void openssl_sha256_init()
{
if (mdctx || basectx)
{
return; // Already initialised
}

mdctx = EVP_MD_CTX_new();
if (mdctx == nullptr)
{
throw std::logic_error("openssl_sha256_init: failed to create mdctx");
}
basectx = EVP_MD_CTX_new();
if (basectx == nullptr)
{
mdctx = nullptr;
throw std::logic_error("openssl_sha256_init: failed to create basectx");
}
if (EVP_DigestInit_ex(basectx, EVP_sha256(), nullptr) != 1)
{
mdctx = nullptr;
basectx = nullptr;
throw std::logic_error("EVP_DigestInit_ex failed");
}
}

void openssl_sha256_shutdown()
{
if (mdctx)
{
EVP_MD_CTX_free(mdctx);
mdctx = nullptr;
}
if (basectx)
{
EVP_MD_CTX_free(basectx);
basectx = nullptr;
}
}

void openssl_sha256(const std::span<const uint8_t>& data, uint8_t* h)
{
const EVP_MD* md = EVP_sha256();
int rc = EVP_Digest(data.data(), data.size(), h, nullptr, md, nullptr);
// EVP_Digest calls are notoriously slow with OpenSSL 3.x (see
// https://github.com/openssl/openssl/issues/19612). Instead, we skip the
// calls to EVP_DigestInit_ex() by keeping 2 static thread-local contexts
// and reusing them between calls. This is about 2x faster than EVP_Digest
// for 128-byte buffers.

if (mdctx == nullptr || basectx == nullptr)
{
throw std::logic_error(
"openssl_sha256 failed: openssl_sha256_init should be called first");
}

int rc = EVP_MD_CTX_copy_ex(mdctx, basectx);
if (rc != 1)
{
throw std::logic_error(fmt::format("EVP_MD_CTX_copy_ex failed: {}", rc));
}
rc = EVP_DigestUpdate(mdctx, data.data(), data.size());
if (rc != 1)
{
throw std::logic_error(fmt::format("EVP_DigestUpdate failed: {}", rc));
}
rc = EVP_DigestFinal_ex(mdctx, h, nullptr);
if (rc != 1)
{
throw std::logic_error(fmt::format("EVP_Digest failed: {}", rc));
throw std::logic_error(fmt::format("EVP_DigestFinal_ex failed: {}", rc));
}
}

Expand Down
2 changes: 2 additions & 0 deletions src/crypto/openssl/hash.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,4 +78,6 @@ namespace crypto
};

void openssl_sha256(const std::span<const uint8_t>& data, uint8_t* h);
void openssl_sha256_init();
void openssl_sha256_shutdown();
}
35 changes: 19 additions & 16 deletions src/crypto/test/bench.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -351,28 +351,31 @@ namespace Hashes
PICOBENCH(sha_512_ossl_100k).PICO_HASH_SUFFIX();
}

PICOBENCH_SUITE("digest sha256");
namespace SHA256_bench
template <size_t size>
static void sha256_bench(picobench::state& s)
{
template <size_t size>
static void sha256_bench(picobench::state& s)
crypto::openssl_sha256_init();

std::vector<uint8_t> v(size);
for (size_t i = 0; i < size; ++i)
{
std::vector<uint8_t> v(size);
for (size_t i = 0; i < size; ++i)
{
v[i] = rand();
}
v[i] = rand();
}

crypto::Sha256Hash h;
crypto::Sha256Hash h;

s.start_timer();
for (size_t i = 0; i < 10; ++i)
{
crypto::openssl_sha256(v, h.h.data());
}
s.stop_timer();
s.start_timer();
for (size_t i = 0; i < 10; ++i)
{
crypto::openssl_sha256(v, h.h.data());
}
s.stop_timer();
crypto::openssl_sha256_shutdown();
}

PICOBENCH_SUITE("digest sha256");
namespace SHA256_bench
{
auto openssl_sha256_base = sha256_bench<2 << 6>;
PICOBENCH(openssl_sha256_base).PICO_HASH_SUFFIX();

Expand Down
2 changes: 2 additions & 0 deletions src/crypto/test/crypto.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -881,6 +881,7 @@ TEST_CASE("PEM to JWK and back")

TEST_CASE("Incremental hash")
{
crypto::openssl_sha256_init();
auto simple_hash = crypto::Sha256Hash(contents);

INFO("Incremental hash");
Expand Down Expand Up @@ -921,4 +922,5 @@ TEST_CASE("Incremental hash")
REQUIRE_THROWS_AS(ihash->finalise(), std::logic_error);
}
}
crypto::openssl_sha256_shutdown();
}
10 changes: 10 additions & 0 deletions src/enclave/enclave.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "ccf/node_subsystem_interface.h"
#include "ccf/pal/enclave.h"
#include "ccf/pal/mem.h"
#include "crypto/openssl/hash.h"
#include "ds/oversized.h"
#include "enclave_time.h"
#include "indexing/enclave_lfs_access.h"
Expand Down Expand Up @@ -94,6 +95,7 @@ namespace ccf
{
ccf::pal::initialize_enclave();
ccf::initialize_verifiers();
crypto::openssl_sha256_init();

// From
// https://software.intel.com/content/www/us/en/develop/articles/how-to-use-the-rdrand-engine-in-openssl-for-random-number-generation.html
Expand Down Expand Up @@ -195,6 +197,7 @@ namespace ccf
LOG_TRACE_FMT("Shutting down enclave");
ccf::shutdown_verifiers();
ccf::pal::shutdown_enclave();
crypto::openssl_sha256_shutdown();
}

CreateNodeStatus create_new_node(
Expand Down Expand Up @@ -275,6 +278,7 @@ namespace ccf

bool run_main()
{
crypto::openssl_sha256_init();
LOG_DEBUG_FMT("Running main thread");
#ifndef VIRTUAL_ENCLAVE
try
Expand Down Expand Up @@ -473,6 +477,8 @@ namespace ccf
LOG_INFO_FMT("Enclave stopped successfully. Stopping host...");
RINGBUFFER_WRITE_MESSAGE(AdminMessage::stopped, to_host);

crypto::openssl_sha256_shutdown();

return true;
}
#ifndef VIRTUAL_ENCLAVE
Expand All @@ -483,6 +489,7 @@ namespace ccf
// exceptions bubble up to here and cause the node to shutdown.
RINGBUFFER_WRITE_MESSAGE(
AdminMessage::fatal_error_msg, to_host, std::string(e.what()));
crypto::openssl_sha256_shutdown();
return false;
}
#endif
Expand All @@ -500,6 +507,7 @@ namespace ccf

bool run_worker()
{
crypto::openssl_sha256_init();
LOG_DEBUG_FMT("Running worker thread");
#ifndef VIRTUAL_ENCLAVE
try
Expand All @@ -511,12 +519,14 @@ namespace ccf
msg->data.tid, std::move(msg));

threading::ThreadMessaging::instance().run();
crypto::openssl_sha256_shutdown();
}
#ifndef VIRTUAL_ENCLAVE
catch (const std::exception& e)
{
RINGBUFFER_WRITE_MESSAGE(
AdminMessage::fatal_error_msg, to_host, std::string(e.what()));
crypto::openssl_sha256_shutdown();
return false;
}
#endif
Expand Down
3 changes: 3 additions & 0 deletions src/host/test/ledger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#include "ccf/crypto/sha256_hash.h"
#include "ccf/ds/logger.h"
#include "crypto/openssl/hash.h"
#include "ds/files.h"
#include "ds/serialized.h"
#include "host/snapshots.h"
Expand Down Expand Up @@ -1725,9 +1726,11 @@ TEST_CASE("Ledger init with existing files")
int main(int argc, char** argv)
{
logger::config::default_init();
crypto::openssl_sha256_init();
doctest::Context context;
context.applyCommandLine(argc, argv);
int res = context.run();
crypto::openssl_sha256_shutdown();
if (context.shouldExit())
return res;
return res;
Expand Down
5 changes: 5 additions & 0 deletions src/kv/test/kv_contention.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the Apache 2.0 License.
#include "ccf/ds/logger.h"
#include "crypto/openssl/hash.h"
#include "kv/compacted_version_conflict.h"
#include "kv/kv_serialiser.h"
#include "kv/store.h"
Expand Down Expand Up @@ -79,6 +80,7 @@ DOCTEST_TEST_CASE("Concurrent kv access" * doctest::test_suite("concurrency"))
}

auto thread_fn = [](void* a) {
crypto::openssl_sha256_init();
auto args = static_cast<ThreadArgs*>(a);

for (size_t i = 0u; i < tx_count; ++i)
Expand Down Expand Up @@ -137,6 +139,7 @@ DOCTEST_TEST_CASE("Concurrent kv access" * doctest::test_suite("concurrency"))

// Notify that this thread has finished
--*args->counter;
crypto::openssl_sha256_shutdown();
};

// Start a thread which continually compacts at the latest version, until all
Expand Down Expand Up @@ -265,6 +268,7 @@ DOCTEST_TEST_CASE(
std::atomic<size_t> conflict_count = 0;

auto point_at_previous_write = [&]() {
crypto::openssl_sha256_init();
auto sleep_time = std::chrono::microseconds(5);
while (true)
{
Expand Down Expand Up @@ -306,6 +310,7 @@ DOCTEST_TEST_CASE(
sleep_time =
std::chrono::microseconds((size_t)(sleep_time.count() * factor));
}
crypto::openssl_sha256_shutdown();
};

std::vector<std::thread> threads;
Expand Down
3 changes: 3 additions & 0 deletions src/kv/test/kv_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "ccf/kv/map.h"
#include "ccf/kv/set.h"
#include "ccf/kv/value.h"
#include "crypto/openssl/hash.h"
#include "kv/compacted_version_conflict.h"
#include "kv/kv_serialiser.h"
#include "kv/store.h"
Expand Down Expand Up @@ -3255,9 +3256,11 @@ TEST_CASE("Ledger entry chunk request")
int main(int argc, char** argv)
{
logger::config::default_init();
crypto::openssl_sha256_init();
doctest::Context context;
context.applyCommandLine(argc, argv);
int res = context.run();
crypto::openssl_sha256_shutdown();
if (context.shouldExit())
return res;
return res;
Expand Down
Loading

0 comments on commit 6d9bcf6

Please sign in to comment.