Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 0 additions & 20 deletions include/ccf/crypto/openssl_init.h

This file was deleted.

95 changes: 41 additions & 54 deletions src/crypto/openssl/hash.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@

#include "crypto/openssl/hash.h"

#include "ccf/crypto/openssl_init.h"

#include <limits>
#include <openssl/evp.h>
#include <openssl/sha.h>
Expand Down Expand Up @@ -53,67 +51,56 @@ namespace ccf::crypto
}
}

using namespace OpenSSL;

namespace
void openssl_sha256(const std::span<const uint8_t>& data, uint8_t* h)
{
thread_local EVP_MD_CTX* mdctx = nullptr;
thread_local EVP_MD_CTX* basectx = nullptr;
}

void openssl_sha256_init()
{
if (mdctx != nullptr || basectx != nullptr)
struct Sha256Context
{
return; // Already initialised
}
Sha256Context()
{
mdctx = EVP_MD_CTX_new();
if (mdctx == nullptr)
{
throw std::logic_error("failed to create mdctx");
}
basectx = EVP_MD_CTX_new();
if (basectx == nullptr)
{
mdctx = nullptr;
throw std::logic_error("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");
}
}

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");
}
}
~Sha256Context()
{
if (mdctx != nullptr)
{
EVP_MD_CTX_free(mdctx);
mdctx = nullptr;
}
if (basectx != nullptr)
{
EVP_MD_CTX_free(basectx);
basectx = nullptr;
}
}

void openssl_sha256_shutdown()
{
if (mdctx != nullptr)
{
EVP_MD_CTX_free(mdctx);
mdctx = nullptr;
}
if (basectx != nullptr)
{
EVP_MD_CTX_free(basectx);
basectx = nullptr;
}
}
Sha256Context(const Sha256Context&) = delete;
Sha256Context& operator=(const Sha256Context&) = delete;

void openssl_sha256(const std::span<const uint8_t>& data, uint8_t* h)
{
// 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.
Sha256Context(Sha256Context&&) = delete;
Sha256Context& operator=(Sha256Context&&) = delete;
};

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

int rc = EVP_MD_CTX_copy_ex(mdctx, basectx);
if (rc != 1)
Expand Down
151 changes: 125 additions & 26 deletions src/crypto/test/bench.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
#include "ccf/crypto/hash_provider.h"
#include "ccf/crypto/hmac.h"
#include "ccf/crypto/key_pair.h"
#include "ccf/crypto/openssl_init.h"
#include "ccf/crypto/sha256.h"
#include "ccf/crypto/symmetric_key.h"
#include "crypto/openssl/base64.h"
Expand Down Expand Up @@ -340,46 +339,147 @@ namespace Hashes
PICOBENCH(sha_512_ossl_100k).PICO_HASH_SUFFIX();
}

template <size_t size>
static constexpr auto sha256_repeats = 10;

namespace sha256_external_ctx
{
using namespace OpenSSL;

namespace
{
thread_local EVP_MD_CTX* mdctx = nullptr;
thread_local EVP_MD_CTX* basectx = nullptr;
}

void openssl_sha256_init()
{
if (mdctx != nullptr || basectx != nullptr)
{
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 != nullptr)
{
EVP_MD_CTX_free(mdctx);
mdctx = nullptr;
}
if (basectx != nullptr)
{
EVP_MD_CTX_free(basectx);
basectx = nullptr;
}
}

void openssl_sha256(const std::span<const uint8_t>& data, uint8_t* h)
{
// 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_DigestFinal_ex failed: {}", rc));
}
}
}

static void sha256_bench(picobench::state& s)
{
ccf::crypto::openssl_sha256_init();
sha256_external_ctx::openssl_sha256_init();

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

ccf::crypto::Sha256Hash h;

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

sha256_external_ctx::openssl_sha256_shutdown();
}

static void sha256_bench_(picobench::state& s)
{
std::vector<uint8_t> v(s.iterations());
for (size_t i = 0; i < v.size(); ++i)
{
v[i] = rand();
}

ccf::crypto::Sha256Hash h;

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

// Variant of the code above that uses the OpenSSL API
// directly without any MD or CTX caching/pre-creation
// for comparison. This is fine for larger inputs, but
// substantially slower for smaller inputs, such as
// digests in Merkle Trees.
template <size_t size>
static void sha256_noopt_bench(picobench::state& s)
{
std::vector<uint8_t> v(size);
for (size_t i = 0; i < size; ++i)
std::vector<uint8_t> v(s.iterations());
for (size_t i = 0; i < v.size(); ++i)
{
v[i] = rand();
}

std::vector<uint8_t> out(EVP_MD_size(EVP_sha256()));

s.start_timer();
for (size_t i = 0; i < 10; ++i)
for (auto i = 0; i < sha256_repeats; ++i)
{
auto* md = EVP_MD_fetch(nullptr, "SHA2-256", nullptr);
auto* ctx = EVP_MD_CTX_new();
Expand All @@ -392,22 +492,21 @@ static void sha256_noopt_bench(picobench::state& s)
s.stop_timer();
}

#define DEFINE_SHA256_BENCH(SHIFT) \
PICOBENCH_SUITE("digest sha256 (2 << " #SHIFT ")"); \
namespace SHA256_bench_##SHIFT \
{ \
auto openssl_sha256_##SHIFT##_no = sha256_noopt_bench<2 << SHIFT>; \
PICOBENCH(openssl_sha256_##SHIFT##_no).PICO_HASH_SUFFIX().baseline(); \
auto openssl_sha256_##SHIFT = sha256_bench<2 << SHIFT>; \
PICOBENCH(openssl_sha256_##SHIFT).PICO_HASH_SUFFIX(); \
}
PICOBENCH_SUITE("digest sha256");
namespace SHA256_bench
{
const std::vector<int> sha256_shifts = {
2 << 4, 2 << 6, 2 << 8, 2 << 10, 2 << 12, 2 << 14, 2 << 16};

DEFINE_SHA256_BENCH(6)
DEFINE_SHA256_BENCH(8)
DEFINE_SHA256_BENCH(10)
DEFINE_SHA256_BENCH(12)
DEFINE_SHA256_BENCH(14)
DEFINE_SHA256_BENCH(16)
auto openssl_sha256_noopt = sha256_noopt_bench;
PICOBENCH(openssl_sha256_noopt).iterations(sha256_shifts).baseline();

auto openssl_sha256_opt = sha256_bench;
PICOBENCH(openssl_sha256_opt).iterations(sha256_shifts);

auto openssl_sha256_tl = sha256_bench_;
PICOBENCH(openssl_sha256_tl).iterations(sha256_shifts);
}

PICOBENCH_SUITE("base64");
namespace Base64_bench
Expand Down
3 changes: 0 additions & 3 deletions src/crypto/test/crypto.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
#include "ccf/crypto/jwk.h"
#include "ccf/crypto/key_pair.h"
#include "ccf/crypto/key_wrap.h"
#include "ccf/crypto/openssl_init.h"
#include "ccf/crypto/rsa_key_pair.h"
#include "ccf/crypto/symmetric_key.h"
#include "ccf/crypto/verifier.h"
Expand Down Expand Up @@ -1150,7 +1149,6 @@ TEST_CASE("PEM to JWK and back")

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

INFO("Incremental hash");
Expand Down Expand Up @@ -1191,7 +1189,6 @@ TEST_CASE("Incremental hash")
REQUIRE_THROWS_AS(ihash->finalise(), std::logic_error);
}
}
ccf::crypto::openssl_sha256_shutdown();
}

TEST_CASE("Sign and verify with RSA key")
Expand Down
Loading