Hash harmonisation and signature exploration. Fixes #2106. (#2123)

This commit is contained in:
Christoph M. Wintersteiger 2021-02-18 15:13:14 +00:00 коммит произвёл GitHub
Родитель 4cf0b30515
Коммит cd5792537a
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
55 изменённых файлов: 2529 добавлений и 1755 удалений

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

@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
## [0.18.2]
### Changed
- CCF now supports OpenSSL for many crypto tasks like hashing, signing, and signature verification (#2123).
## [0.18.1]
### Changed

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

@ -89,7 +89,6 @@ if("sgx" IN_LIST COMPILE_TARGETS)
ccfcrypto.enclave
http_parser.enclave
lua.enclave
secp256k1.enclave
aft.enclave
sss.enclave
$<BUILD_INTERFACE:merklecpp>
@ -139,7 +138,6 @@ if("virtual" IN_LIST COMPILE_TARGETS)
ccfcrypto.host
http_parser.host
lua.host
secp256k1.host
aft.virtual
sss.host
openenclave::oehostverify
@ -256,7 +254,7 @@ if(BUILD_TESTS)
)
use_client_mbedtls(kv_test)
target_link_libraries(
kv_test PRIVATE ${CMAKE_THREAD_LIBS_INIT} secp256k1.host http_parser.host
kv_test PRIVATE ${CMAKE_THREAD_LIBS_INIT} http_parser.host
)
add_unit_test(
@ -279,16 +277,14 @@ if(BUILD_TESTS)
raft_test ${CMAKE_CURRENT_SOURCE_DIR}/src/consensus/aft/test/main.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/consensus/aft/test/view_history.cpp
)
target_link_libraries(raft_test PRIVATE ${CRYPTO_LIBRARY} secp256k1.host)
target_link_libraries(raft_test PRIVATE ${CRYPTO_LIBRARY})
add_unit_test(
raft_enclave_test
${CMAKE_CURRENT_SOURCE_DIR}/src/consensus/aft/test/enclave.cpp
)
target_include_directories(raft_enclave_test PRIVATE ${CCFCRYPTO_INC})
target_link_libraries(
raft_enclave_test PRIVATE ${CRYPTO_LIBRARY} secp256k1.host
)
target_link_libraries(raft_enclave_test PRIVATE ${CRYPTO_LIBRARY})
add_unit_test(
crypto_test ${CMAKE_CURRENT_SOURCE_DIR}/src/crypto/test/crypto.cpp
@ -300,16 +296,14 @@ if(BUILD_TESTS)
history_test ${CMAKE_CURRENT_SOURCE_DIR}/src/node/test/history.cpp
)
target_link_libraries(
history_test PRIVATE ${CRYPTO_LIBRARY} secp256k1.host http_parser.host
history_test PRIVATE ${CRYPTO_LIBRARY} http_parser.host
)
add_unit_test(
progress_tracker_test
${CMAKE_CURRENT_SOURCE_DIR}/src/node/test/progress_tracker.cpp
)
target_link_libraries(
progress_tracker_test PRIVATE ${CRYPTO_LIBRARY} secp256k1.host
)
target_link_libraries(progress_tracker_test PRIVATE ${CRYPTO_LIBRARY})
add_unit_test(
secret_sharing_test
@ -322,26 +316,24 @@ if(BUILD_TESTS)
${CMAKE_CURRENT_SOURCE_DIR}/src/crypto/symmetric_key.cpp
)
use_client_mbedtls(encryptor_test)
target_link_libraries(encryptor_test PRIVATE secp256k1.host)
target_link_libraries(encryptor_test PRIVATE)
add_unit_test(
historical_queries_test
${CMAKE_CURRENT_SOURCE_DIR}/src/node/test/historical_queries.cpp
)
target_link_libraries(
historical_queries_test PRIVATE secp256k1.host http_parser.host
)
target_link_libraries(historical_queries_test PRIVATE http_parser.host)
add_unit_test(
snapshot_test ${CMAKE_CURRENT_SOURCE_DIR}/src/node/test/snapshot.cpp
)
target_link_libraries(snapshot_test PRIVATE secp256k1.host)
target_link_libraries(snapshot_test PRIVATE)
add_unit_test(
snapshotter_test
${CMAKE_CURRENT_SOURCE_DIR}/src/node/test/snapshotter.cpp
)
target_link_libraries(snapshotter_test PRIVATE secp256k1.host)
target_link_libraries(snapshotter_test PRIVATE)
add_unit_test(
msgpack_serialization_test
@ -349,40 +341,36 @@ if(BUILD_TESTS)
)
add_unit_test(tls_test ${CMAKE_CURRENT_SOURCE_DIR}/src/tls/test/main.cpp)
target_link_libraries(
tls_test PRIVATE ${CMAKE_THREAD_LIBS_INIT} secp256k1.host
)
target_link_libraries(tls_test PRIVATE ${CMAKE_THREAD_LIBS_INIT})
add_test_bin(cert_test ${CMAKE_CURRENT_SOURCE_DIR}/src/tls/test/cert.cpp)
target_link_libraries(
cert_test PRIVATE ${CMAKE_THREAD_LIBS_INIT} secp256k1.host
)
target_link_libraries(cert_test PRIVATE ${CMAKE_THREAD_LIBS_INIT})
add_unit_test(
key_exchange_test
${CMAKE_CURRENT_SOURCE_DIR}/src/tls/test/key_exchange.cpp
)
use_client_mbedtls(key_exchange_test)
target_link_libraries(key_exchange_test PRIVATE secp256k1.host)
target_link_libraries(key_exchange_test PRIVATE)
add_unit_test(
channels_test ${CMAKE_CURRENT_SOURCE_DIR}/src/node/test/channels.cpp
)
use_client_mbedtls(channels_test)
target_link_libraries(channels_test PRIVATE secp256k1.host)
target_link_libraries(channels_test PRIVATE)
add_unit_test(
http_test ${CMAKE_CURRENT_SOURCE_DIR}/src/http/test/http_test.cpp
)
target_link_libraries(http_test PRIVATE http_parser.host secp256k1.host)
target_link_libraries(http_test PRIVATE http_parser.host)
add_unit_test(
frontend_test
${CMAKE_CURRENT_SOURCE_DIR}/src/node/rpc/test/frontend_test.cpp
)
target_link_libraries(
frontend_test PRIVATE ${CMAKE_THREAD_LIBS_INIT} lua.host secp256k1.host
http_parser.host sss.host
frontend_test PRIVATE ${CMAKE_THREAD_LIBS_INIT} lua.host http_parser.host
sss.host
)
add_unit_test(
@ -396,7 +384,7 @@ if(BUILD_TESTS)
)
target_link_libraries(
member_voting_test PRIVATE ${CMAKE_THREAD_LIBS_INIT} lua.host
secp256k1.host http_parser.host sss.host
http_parser.host sss.host
)
add_unit_test(
@ -405,7 +393,7 @@ if(BUILD_TESTS)
)
target_link_libraries(
proposal_id_test PRIVATE ${CMAKE_THREAD_LIBS_INIT} lua.host
secp256k1.host http_parser.host sss.host
http_parser.host sss.host
)
add_unit_test(
@ -414,7 +402,7 @@ if(BUILD_TESTS)
)
target_link_libraries(
node_frontend_test PRIVATE ${CMAKE_THREAD_LIBS_INIT} lua.host
secp256k1.host http_parser.host sss.host
http_parser.host sss.host
)
if(NOT ENV{RUNTIME_CONFIG_DIR})
@ -430,9 +418,7 @@ if(BUILD_TESTS)
${CMAKE_CURRENT_SOURCE_DIR}/src/lua_interp/test/lua_kv.cpp
)
target_include_directories(lua_test PRIVATE ${LUA_DIR})
target_link_libraries(
lua_test PRIVATE lua.host http_parser.host secp256k1.host
)
target_link_libraries(lua_test PRIVATE lua.host http_parser.host)
add_unit_test(
merkle_test ${CMAKE_CURRENT_SOURCE_DIR}/src/node/test/merkle_test.cpp
@ -441,9 +427,8 @@ 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> crypto
merkle_mem PRIVATE ccfcrypto.host ${CMAKE_THREAD_LIBS_INIT}
$<BUILD_INTERFACE:merklecpp> crypto
)
use_client_mbedtls(merkle_mem)
@ -455,7 +440,7 @@ if(BUILD_TESTS)
add_executable(
raft_driver ${CMAKE_CURRENT_SOURCE_DIR}/src/consensus/aft/test/driver.cpp
)
target_link_libraries(raft_driver PRIVATE ccfcrypto.host secp256k1.host)
target_link_libraries(raft_driver PRIVATE ccfcrypto.host)
use_client_mbedtls(raft_driver)
target_include_directories(raft_driver PRIVATE src/aft)
@ -486,29 +471,29 @@ if(BUILD_TESTS)
add_picobench(
tls_bench
SRCS src/tls/test/bench.cpp
LINK_LIBS secp256k1.host
LINK_LIBS
)
add_picobench(
merkle_bench
SRCS src/node/test/merkle_bench.cpp
LINK_LIBS ccfcrypto.host secp256k1.host crypto
LINK_LIBS ccfcrypto.host crypto
)
add_picobench(
history_bench
SRCS src/node/test/history_bench.cpp
LINK_LIBS ccfcrypto.host secp256k1.host crypto
LINK_LIBS ccfcrypto.host crypto
)
add_picobench(
kv_bench
SRCS src/kv/test/kv_bench.cpp src/crypto/symmetric_key.cpp
src/enclave/thread_local.cpp
LINK_LIBS ccfcrypto.host secp256k1.host
LINK_LIBS ccfcrypto.host
)
add_picobench(hash_bench SRCS src/ds/test/hash_bench.cpp)
add_picobench(
digest_bench
SRCS src/crypto/test/digest_bench.cpp
LINK_LIBS ccfcrypto.host secp256k1.host
LINK_LIBS ccfcrypto.host
)
# Storing signed governance operations

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

@ -183,7 +183,6 @@ set(HTTP_PARSER_SOURCES
find_library(CRYPTO_LIBRARY crypto)
include(${CCF_DIR}/cmake/crypto.cmake)
include(${CCF_DIR}/cmake/secp256k1.cmake)
include(${CCF_DIR}/cmake/quickjs.cmake)
include(${CCF_DIR}/cmake/sss.cmake)
@ -293,8 +292,8 @@ add_executable(
)
use_client_mbedtls(scenario_perf_client)
target_link_libraries(
scenario_perf_client PRIVATE ${CMAKE_THREAD_LIBS_INIT} secp256k1.host
http_parser.host ccfcrypto.host
scenario_perf_client PRIVATE ${CMAKE_THREAD_LIBS_INIT} http_parser.host
ccfcrypto.host
)
install(TARGETS scenario_perf_client DESTINATION bin)
@ -593,7 +592,7 @@ function(add_picobench name)
target_link_libraries(
${name} PRIVATE ${CMAKE_THREAD_LIBS_INIT} ${PARSED_ARGS_LINK_LIBS}
$<BUILD_INTERFACE:merklecpp>
$<BUILD_INTERFACE:merklecpp> crypto
)
# -Wall -Werror catches a number of warnings in picobench

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

@ -28,8 +28,8 @@ endif()
add_library(ccfcrypto.host STATIC ${CCFCRYPTO_SRC})
add_san(ccfcrypto.host)
target_compile_definitions(ccfcrypto.host PRIVATE)
target_compile_options(ccfcrypto.host PRIVATE -stdlib=libc++)
target_link_libraries(ccfcrypto.host PRIVATE crypto)
use_client_mbedtls(ccfcrypto.host)
set_property(TARGET ccfcrypto.host PROPERTY POSITION_INDEPENDENT_CODE ON)

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

@ -11,8 +11,6 @@ import tempfile
from dataclasses import dataclass
from http.client import HTTPResponse
from io import BytesIO
from requests.adapters import HTTPAdapter
from urllib3.util.ssl_ import create_urllib3_context # type: ignore
from cryptography import x509
from cryptography.hazmat.backends import default_backend
import struct
@ -269,13 +267,7 @@ class CurlClient:
self.ca = ca
self.session_auth = session_auth
self.signing_auth = signing_auth
ca_curve = get_curve(self.ca)
if ca_curve.name == "secp256k1":
raise RuntimeError(
f"CurlClient cannot perform TLS handshake with {ca_curve.name} ECDH curve. "
"Use RequestClient class instead."
)
self.ca_curve = get_curve(self.ca)
def request(self, request, timeout=DEFAULT_REQUEST_TIMEOUT_SEC):
with tempfile.NamedTemporaryFile() as nf:
@ -353,26 +345,6 @@ class CurlClient:
return Response.from_raw(rc.stdout)
class TlsAdapter(HTTPAdapter):
"""
Support for secp256k1 as node and network identity curve.
"""
def __init__(self, ca_file):
self.ca_curve = None
if ca_file is not None:
self.ca_curve = get_curve(ca_file)
super().__init__()
# pylint: disable=signature-differs
def init_poolmanager(self, *args, **kwargs):
if self.ca_curve is not None:
context = create_urllib3_context()
context.set_ecdh_curve(self.ca_curve.name)
kwargs["ssl_context"] = context
return super(TlsAdapter, self).init_poolmanager(*args, **kwargs)
class HTTPSignatureAuth_AlwaysDigest(HTTPSignatureAuth):
"""
Support for HTTP signatures with empty body.
@ -417,7 +389,6 @@ class RequestClient:
if self.signing_auth:
with open(self.signing_auth.cert) as cert_file:
self.key_id = hashlib.sha256(cert_file.read().encode()).hexdigest()
self.session.mount("https://", TlsAdapter(self.ca))
def request(
self,
@ -512,13 +483,7 @@ class WSClient:
self.ca = ca
self.session_auth = session_auth
self.ws = None
ca_curve = get_curve(self.ca)
if ca_curve.name == "secp256k1":
raise RuntimeError(
f"WSClient cannot perform TLS handshake with {ca_curve.name} ECDH curve. "
"Use RequestClient class instead."
)
self.ca_curve = get_curve(self.ca)
def request(
self,

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

@ -5,7 +5,7 @@
set -e
DEFAULT_CURVE="secp384r1"
FAST_CURVE="secp256k1"
FAST_CURVE="secp256r1"
SUPPORTED_CURVES="$DEFAULT_CURVE|$FAST_CURVE"
DIGEST_SHA384="sha384"

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

@ -7,16 +7,14 @@ 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 crypto
LINK_LIBS ccfcrypto.host crypto
)
add_client_exe(
small_bank_client
SRCS ${CMAKE_CURRENT_LIST_DIR}/clients/small_bank_client.cpp
)
target_link_libraries(
small_bank_client PRIVATE secp256k1.host http_parser.host ccfcrypto.host
)
target_link_libraries(small_bank_client PRIVATE http_parser.host ccfcrypto.host)
# SmallBank application
add_ccf_app(smallbank SRCS ${CMAKE_CURRENT_LIST_DIR}/app/smallbank.cpp)
@ -95,7 +93,7 @@ if(BUILD_TESTS)
1000
--sign
--participants-curve
"secp256k1"
"secp256r1"
--metrics-file
small_bank_cft_sigs_metrics.json
)

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

@ -68,8 +68,7 @@ namespace aft
auto data_ = raw_request.data();
auto size_ = raw_request.size();
crypto::Sha256Hash hash;
tls::do_hash(data_, size_, hash.h, MBEDTLS_MD_SHA256);
crypto::Sha256Hash hash({data_, size_});
if (!request_tracker->remove(hash))
{

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

@ -5,8 +5,8 @@
#include "../tls/mbedtls_wrappers.h"
#include <mbedtls/sha256.h>
#include <openssl/sha.h>
#include <stdexcept>
using namespace std;
namespace crypto
@ -23,57 +23,83 @@ namespace crypto
mbedtls_sha256_free(&ctx);
}
class MBSha256HashImpl
void Sha256Hash::openssl_sha256(const CBuffer& data, uint8_t* h)
{
public:
MBSha256HashImpl()
{
ctx = std::move(mbedtls::make_unique<mbedtls::SHA256Ctx>());
mbedtls_sha256_starts_ret(ctx.get(), 0);
}
void finalize(std::array<uint8_t, Sha256Hash::SIZE>& h)
{
mbedtls_sha256_finish_ret(ctx.get(), h.data());
}
void update(const CBuffer& data)
{
mbedtls_sha256_update_ret(ctx.get(), data.p, data.rawSize());
}
private:
mbedtls::SHA256Ctx ctx;
};
Sha256Hash::Sha256Hash() : h{0} {}
Sha256Hash::Sha256Hash(const CBuffer& data) : h{0}
{
mbedtls_sha256(data, h.data());
SHA256_CTX ctx;
SHA256_Init(&ctx);
SHA256_Update(&ctx, data.p, data.rawSize());
SHA256_Final(h, &ctx);
}
CSha256Hash::CSha256Hash() : p(std::make_unique<MBSha256HashImpl>()) {}
CSha256Hash::~CSha256Hash() {}
void CSha256Hash::update_hash(CBuffer data)
ISha256MbedTLS::ISha256MbedTLS()
{
if (p == nullptr)
{
throw std::logic_error("Attempting to use hash after it was finalized");
}
p->update(data);
ctx = new mbedtls_sha256_context();
mbedtls_sha256_starts_ret((mbedtls_sha256_context*)ctx, 0);
}
Sha256Hash CSha256Hash::finalize()
ISha256MbedTLS::~ISha256MbedTLS()
{
if (p == nullptr)
delete (mbedtls_sha256_context*)ctx;
}
Sha256Hash ISha256MbedTLS::finalise()
{
if (!ctx)
{
throw std::logic_error("Attempting to use hash after it was finalized");
throw std::logic_error("Attempting to use hash after it was finalised");
}
Sha256Hash h;
p->finalize(h.h);
p = nullptr;
return h;
Sha256Hash r;
mbedtls_sha256_finish_ret((mbedtls_sha256_context*)ctx, r.h.data());
mbedtls_sha256_free((mbedtls_sha256_context*)ctx);
delete (mbedtls_sha256_context*)ctx;
ctx = nullptr;
return r;
}
void ISha256MbedTLS::update_hash(CBuffer data)
{
if (!ctx)
{
throw std::logic_error("Attempting to use hash after it was finalised");
}
mbedtls_sha256_update_ret(
(mbedtls_sha256_context*)ctx, data.p, data.rawSize());
}
ISha256OpenSSL::ISha256OpenSSL()
{
ctx = new SHA256_CTX;
SHA256_Init((SHA256_CTX*)ctx);
}
ISha256OpenSSL::~ISha256OpenSSL()
{
delete (SHA256_CTX*)ctx;
}
void ISha256OpenSSL::update_hash(CBuffer data)
{
if (!ctx)
{
throw std::logic_error("Attempting to use hash after it was finalised");
}
SHA256_Update((SHA256_CTX*)ctx, data.p, data.rawSize());
}
Sha256Hash ISha256OpenSSL::finalise()
{
if (!ctx)
{
throw std::logic_error("Attempting to use hash after it was finalised");
}
Sha256Hash r;
SHA256_Final(r.h.data(), (SHA256_CTX*)ctx);
delete (SHA256_CTX*)ctx;
ctx = nullptr;
return r;
}
}

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

@ -4,6 +4,11 @@
#include "ds/buffer.h"
#include "ds/json.h"
#include <mbedtls/md.h>
#include <mbedtls/pk.h>
#include <openssl/evp.h>
#include <openssl/sha.h>
#define FMT_HEADER_ONLY
#include <fmt/format.h>
#include <msgpack/msgpack.hpp>
@ -11,16 +16,114 @@
namespace crypto
{
enum class MDType
{
NONE = 0,
SHA1,
SHA256,
SHA384,
SHA512
};
using HashBytes = std::vector<uint8_t>;
class HashProviderBase
{
public:
virtual HashBytes Hash(const uint8_t*, size_t, MDType) const = 0;
};
class MBedHashProvider : public HashProviderBase
{
public:
static inline mbedtls_md_type_t get_md_type(MDType type)
{
switch (type)
{
case MDType::NONE:
return MBEDTLS_MD_NONE;
case MDType::SHA1:
return MBEDTLS_MD_SHA1;
case MDType::SHA256:
return MBEDTLS_MD_SHA256;
case MDType::SHA384:
return MBEDTLS_MD_SHA384;
case MDType::SHA512:
return MBEDTLS_MD_SHA512;
default:
throw std::runtime_error("Unsupported hash algorithm");
}
return MBEDTLS_MD_NONE;
}
virtual HashBytes Hash(const uint8_t* data, size_t size, MDType type) const
{
HashBytes r;
const auto mbedtls_md_type = get_md_type(type);
const auto info = mbedtls_md_info_from_type(mbedtls_md_type);
const auto hash_size = mbedtls_md_get_size(info);
r.resize(hash_size);
if (mbedtls_md(info, data, size, r.data()) != 0)
r.clear();
return r;
}
};
class OpenSSLHashProvider : public HashProviderBase
{
public:
static inline const EVP_MD* get_md_type(MDType type)
{
switch (type)
{
case MDType::NONE:
return nullptr;
case MDType::SHA1:
return EVP_sha1();
case MDType::SHA256:
return EVP_sha256();
case MDType::SHA384:
return EVP_sha384();
case MDType::SHA512:
return EVP_sha512();
default:
throw std::runtime_error("Unsupported hash algorithm");
}
return nullptr;
}
virtual HashBytes Hash(const uint8_t* data, size_t size, MDType type) const
{
auto o_md_type = get_md_type(type);
HashBytes r(EVP_MD_size(o_md_type));
unsigned int len = 0;
if (EVP_Digest(data, size, r.data(), &len, o_md_type, NULL) != 1)
throw std::runtime_error("OpenSSL hash update error");
return r;
}
};
typedef MBedHashProvider HashProvider;
class Sha256Hash
{
public:
static constexpr size_t SIZE = 256 / 8;
Sha256Hash();
Sha256Hash(const CBuffer& data);
Sha256Hash() : h{0} {}
Sha256Hash(const CBuffer& data) : h{0}
{
::SHA256(data.p, data.rawSize(), h.data());
}
std::array<uint8_t, SIZE> h;
static void mbedtls_sha256(const CBuffer& data, uint8_t* h);
static void openssl_sha256(const CBuffer& data, uint8_t* h);
friend std::ostream& operator<<(
std::ostream& os, const crypto::Sha256Hash& h)
@ -61,14 +164,15 @@ namespace crypto
return !(lhs == rhs);
}
class MBSha256HashImpl;
class CSha256Hash
// Incremental Hash Objects
class ISha256HashBase
{
public:
CSha256Hash();
~CSha256Hash();
ISha256HashBase() {}
virtual ~ISha256HashBase() {}
void update_hash(CBuffer data);
virtual void update_hash(CBuffer data) = 0;
virtual Sha256Hash finalise() = 0;
template <typename T>
void update(const T& t)
@ -81,12 +185,33 @@ namespace crypto
{
update_hash({d.data(), d.size()});
}
Sha256Hash finalize();
private:
std::unique_ptr<MBSha256HashImpl> p;
};
class ISha256MbedTLS : public ISha256HashBase
{
public:
ISha256MbedTLS();
~ISha256MbedTLS();
virtual void update_hash(CBuffer data);
virtual Sha256Hash finalise();
protected:
void* ctx;
};
class ISha256OpenSSL : public ISha256HashBase
{
public:
ISha256OpenSSL();
~ISha256OpenSSL();
virtual void update_hash(CBuffer data);
virtual Sha256Hash finalise();
protected:
void* ctx;
};
typedef ISha256OpenSSL ISha256Hash;
}
namespace fmt

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

@ -8,7 +8,8 @@
enum HashImpl
{
mbedtls
mbedtls,
openssl
};
template <HashImpl IMPL>
@ -29,6 +30,10 @@ static void sha256_bench(picobench::state& s)
{
crypto::Sha256Hash::mbedtls_sha256(v, h.h.data());
}
else if constexpr (IMPL == HashImpl::openssl)
{
crypto::Sha256Hash::openssl_sha256(v, h.h.data());
}
}
s.stop_timer();
}
@ -38,4 +43,7 @@ const std::vector<int> hash_sizes = {2 << 6, 2 << 8, 2 << 12, 2 << 16, 2 << 18};
PICOBENCH_SUITE("SHA-256");
auto mbedtls_digest_sha256 = sha256_bench<HashImpl::mbedtls>;
PICOBENCH(mbedtls_digest_sha256).iterations(hash_sizes).baseline();
PICOBENCH(mbedtls_digest_sha256).iterations(hash_sizes).baseline();
auto openssl_digest_sha256 = sha256_bench<HashImpl::openssl>;
PICOBENCH(openssl_digest_sha256).iterations(hash_sizes).baseline();

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

@ -17,6 +17,8 @@
#include "rpc_map.h"
#include "rpc_sessions.h"
#include <openssl/engine.h>
namespace enclave
{
class Enclave
@ -34,6 +36,7 @@ namespace enclave
std::shared_ptr<ccf::Forwarder<ccf::NodeToNode>> cmd_forwarder;
ringbuffer::WriterPtr to_host = nullptr;
std::chrono::milliseconds last_tick_time;
ENGINE* rdrand_engine = nullptr;
CCFConfig ccf_config;
StartType start_type;
@ -63,7 +66,8 @@ namespace enclave
const EnclaveConfig& ec,
const CCFConfig::SignatureIntervals& signature_intervals,
const ConsensusType& consensus_type_,
const consensus::Configuration& consensus_config) :
const consensus::Configuration& consensus_config,
const CurveID& curve_id) :
circuit(
ringbuffer::BufferDef{ec.to_enclave_buffer_start,
ec.to_enclave_buffer_size,
@ -86,10 +90,23 @@ namespace enclave
logger::config::msg() = AdminMessage::log_msg;
logger::config::writer() = writer_factory.create_writer_to_outside();
// From
// https://software.intel.com/content/www/us/en/develop/articles/how-to-use-the-rdrand-engine-in-openssl-for-random-number-generation.html
if (
ENGINE_load_rdrand() != 1 ||
(rdrand_engine = ENGINE_by_id("rdrand")) == nullptr ||
ENGINE_init(rdrand_engine) != 1 ||
ENGINE_set_default(rdrand_engine, ENGINE_METHOD_RAND) != 1)
{
ENGINE_free(rdrand_engine);
throw std::runtime_error(
"could not initialize RDRAND engine for OpenSSL");
}
to_host = writer_factory.create_writer_to_outside();
node = std::make_unique<ccf::NodeState>(
writer_factory, network, rpcsessions, share_manager);
writer_factory, network, rpcsessions, share_manager, curve_id);
context.node_state = node.get();
rpc_map->register_frontend<ccf::ActorsType::members>(
@ -119,6 +136,15 @@ namespace enclave
signature_intervals.sig_ms_interval);
}
~Enclave()
{
if (rdrand_engine)
{
ENGINE_finish(rdrand_engine);
ENGINE_free(rdrand_engine);
}
}
bool create_new_node(
StartType start_type_,
const CCFConfig& ccf_config_,

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

@ -14,6 +14,7 @@
#include "node/members.h"
#include "node/node_info_network.h"
#include "start_type.h"
#include "tls/curve.h"
#include "tls/san.h"
#include "tls/tls.h"
@ -40,6 +41,8 @@ struct EnclaveConfig
#endif
};
MSGPACK_ADD_ENUM(tls::CurveID);
struct CCFConfig
{
consensus::Configuration consensus_config = {};
@ -83,6 +86,8 @@ struct CCFConfig
size_t jwt_key_refresh_interval_s;
tls::CurveID curve_id;
MSGPACK_DEFINE(
consensus_config,
node_info_network,
@ -95,7 +100,8 @@ struct CCFConfig
joining,
subject_name,
subject_alternative_names,
jwt_key_refresh_interval_s);
jwt_key_refresh_interval_s,
curve_id);
};
/// General administrative messages

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

@ -128,7 +128,11 @@ extern "C"
#endif
auto enclave = new enclave::Enclave(
ec, cc.signature_intervals, consensus_type, cc.consensus_config);
ec,
cc.signature_intervals,
consensus_type,
cc.consensus_config,
cc.curve_id);
bool result = enclave->create_new_node(
start_type,

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

@ -422,6 +422,19 @@ int main(int argc, char** argv)
->capture_default_str()
->check(CLI::NonexistentPath);
CurveID curve_id = CurveID::SECP384R1;
std::vector<std::pair<std::string, CurveID>> curve_id_map = {
{"secp384r1", CurveID::SECP384R1}, {"secp256r1", CurveID::SECP256R1}};
app
.add_option(
"--curve-id",
curve_id,
"Elliptic curve to use as for node and network identities (used for TLS "
"and ledger "
"signatures")
->transform(CLI::CheckedTransformer(curve_id_map, CLI::ignore_case))
->capture_default_str();
CLI11_PARSE(app, argc, argv);
if (!(*public_rpc_address_option))
@ -669,6 +682,8 @@ int main(int argc, char** argv)
ccf_config.jwt_key_refresh_interval_s = jwt_key_refresh_interval_s;
ccf_config.curve_id = curve_id;
if (*start)
{
start_type = StartType::New;

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

@ -5,7 +5,6 @@
#include "http_consts.h"
#include "http_status.h"
#include "tls/base64.h"
#include "tls/hash.h"
#define FMT_HEADER_ONLY
#include <fmt/format.h>

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

@ -155,7 +155,7 @@ namespace http
token.signed_content.size(),
token.signature.data(),
token.signature.size(),
MBEDTLS_MD_SHA256);
crypto::MDType::SHA256);
return valid;
}
};

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

@ -2,11 +2,11 @@
// Licensed under the Apache 2.0 License.
#pragma once
#include "crypto/hash.h"
#include "http_consts.h"
#include "http_parser.h"
#include "node/client_signatures.h"
#include "tls/base64.h"
#include "tls/hash.h"
#include "tls/key_pair.h"
#define FMT_HEADER_ONLY
@ -70,18 +70,14 @@ namespace http
inline void add_digest_header(http::Request& request)
{
// Ensure digest is present and up-to-date
tls::HashBytes body_digest;
tls::do_hash(
request.get_content_data(),
request.get_content_length(),
body_digest,
MBEDTLS_MD_SHA256);
crypto::Sha256Hash body_digest(
{request.get_content_data(), request.get_content_length()});
request.set_header(
headers::DIGEST,
fmt::format(
"{}={}",
"SHA-256",
tls::b64_from_raw(body_digest.data(), body_digest.size())));
tls::b64_from_raw(body_digest.h.data(), body_digest.SIZE)));
}
inline void sign_request(
@ -200,16 +196,19 @@ namespace http
auto raw_digest = tls::raw_from_b64(digest->second.substr(equal_pos + 1));
// Then, hash the request body
tls::HashBytes body_digest;
tls::do_hash(body.data(), body.size(), body_digest, MBEDTLS_MD_SHA256);
crypto::Sha256Hash body_digest({body.data(), body.size()});
if (raw_digest != body_digest)
if (!std::equal(
raw_digest.begin(),
raw_digest.end(),
body_digest.h.begin(),
body_digest.h.end()))
{
error_reason = fmt::format(
"Request body does not match {} header, calculated body "
"digest = {:02x}",
headers::DIGEST,
fmt::join(body_digest, ""));
fmt::join(body_digest.h, ""));
return false;
}
@ -416,18 +415,18 @@ namespace http
auto sig_raw = tls::raw_from_b64(parsed_sign_params->signature);
mbedtls_md_type_t signature_digest = MBEDTLS_MD_NONE;
crypto::MDType md_type = crypto::MDType::NONE;
if (
parsed_sign_params->signature_algorithm ==
auth::SIGN_ALGORITHM_ECDSA_SHA256)
{
signature_digest = MBEDTLS_MD_SHA256;
md_type = crypto::MDType::SHA256;
}
ccf::SignedReq ret = {sig_raw,
signed_raw.value(),
body,
signature_digest,
md_type,
parsed_sign_params->key_id};
return ret;
}

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

@ -2,9 +2,9 @@
// Licensed under the Apache 2.0 License.
#pragma once
#include "crypto/hash.h"
#include "http_parser.h"
#include "tls/base64.h"
#include "tls/hash.h"
#include <optional>
#include <string>
@ -25,8 +25,9 @@ namespace http
const auto data = reinterpret_cast<const uint8_t*>(string_to_hash.data());
const auto size = string_to_hash.size();
tls::HashBytes accept_string_hash;
tls::do_hash(data, size, accept_string_hash, MBEDTLS_MD_SHA1);
crypto::HashProvider hp;
crypto::HashBytes accept_string_hash =
hp.Hash(data, size, crypto::MDType::SHA1);
return tls::b64_from_raw(
accept_string_hash.data(), accept_string_hash.size());

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

@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the Apache 2.0 License.
#pragma once
#include "crypto/hash.h"
#include "ds/hash.h"
#include "ds/json.h"
#include "entities.h"
@ -10,15 +11,21 @@
#include <msgpack/msgpack.hpp>
#include <vector>
MSGPACK_ADD_ENUM(mbedtls_md_type_t);
using namespace tls;
using namespace crypto;
DECLARE_JSON_ENUM(
mbedtls_md_type_t,
{{MBEDTLS_MD_NONE, "MBEDTLS_MD_NONE"},
{MBEDTLS_MD_SHA1, "MBEDTLS_MD_SHA1"},
{MBEDTLS_MD_SHA256, "MBEDTLS_MD_SHA256"},
{MBEDTLS_MD_SHA384, "MBEDTLS_MD_SHA384"},
{MBEDTLS_MD_SHA512, "MBEDTLS_MD_SHA512"}});
MSGPACK_ADD_ENUM(MDType);
namespace crypto
{
DECLARE_JSON_ENUM(
MDType,
{{MDType::NONE, "NONE"},
{MDType::SHA1, "SHA1"},
{MDType::SHA256, "SHA256"},
{MDType::SHA384, "SHA384"},
{MDType::SHA512, "SHA512"}});
}
namespace ccf
{
@ -33,7 +40,7 @@ namespace ccf
std::vector<uint8_t> request_body = {};
// signature hashing algorithm used
mbedtls_md_type_t md = MBEDTLS_MD_NONE;
MDType md = MDType::NONE;
// The key id, if declared in the request
std::string key_id = {};

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

@ -109,7 +109,7 @@ namespace ccf
// The key to a CertDERs table must be a DER, for easy comparison against
// the DER peer cert retrieved from the connection
auto member_cert_der =
tls::make_verifier(member_pub_info.cert)->der_cert_data();
tls::make_verifier(member_pub_info.cert)->cert_der();
auto member_id = mc->get(member_cert_der);
if (member_id.has_value())
@ -229,7 +229,7 @@ namespace ccf
auto ud = tx.rw(tables.user_digests);
auto v = tx.rw(tables.values);
auto user_cert_der = tls::make_verifier(user_info.cert)->der_cert_data();
auto user_cert_der = tls::make_verifier(user_info.cert)->cert_der();
// Cert should be unique
auto user_id = uc->get(user_cert_der);
@ -260,7 +260,7 @@ namespace ccf
}
auto pem = tls::Pem(user_info.value().cert);
auto user_cert_der = tls::make_verifier(pem)->der_cert_data();
auto user_cert_der = tls::make_verifier(pem)->cert_der();
u->remove(user_id);
uc->remove(user_cert_der);
@ -271,7 +271,7 @@ namespace ccf
{
auto node_id = get_next_id(tx.rw(tables.values), ValueIds::NEXT_NODE_ID);
auto raw_cert = tls::make_verifier(node_info.cert)->der_cert_data();
auto raw_cert = tls::make_verifier(node_info.cert)->cert_der();
auto node = tx.rw(tables.nodes);
node->put(node_id, node_info);

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

@ -139,11 +139,8 @@ namespace ccf::historical
}
auto verifier = tls::make_verifier(node_info->cert);
const auto verified = verifier->verify_hash(
real_root.h.data(),
real_root.h.size(),
sig->sig.data(),
sig->sig.size());
const auto verified =
verifier->verify_hash(real_root.h, sig->sig, MDType::SHA256);
if (!verified)
{
throw std::logic_error(

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

@ -117,7 +117,7 @@ namespace ccf
kv::Term term = 0;
public:
NullTxHistory(kv::Store& store_, NodeId id_, tls::KeyPair&) :
NullTxHistory(kv::Store& store_, NodeId id_, tls::KeyPairBase&) :
store(store_),
id(id_)
{}
@ -250,7 +250,7 @@ namespace ccf
kv::Store& store;
T& replicated_state_tree;
NodeId id;
tls::KeyPair& kp;
tls::KeyPairBase& kp;
public:
MerkleTreeHistoryPendingTx(
@ -259,7 +259,7 @@ namespace ccf
kv::Store& store_,
T& replicated_state_tree_,
NodeId id_,
tls::KeyPair& kp_) :
tls::KeyPairBase& kp_) :
txid(txid_),
commit_txid(commit_txid_),
store(store_),
@ -462,7 +462,7 @@ namespace ccf
NodeId id;
T replicated_state_tree;
tls::KeyPair& kp;
tls::KeyPairBase& kp;
std::map<RequestID, std::vector<uint8_t>> requests;
@ -477,7 +477,7 @@ namespace ccf
HashedTxHistory(
kv::Store& store_,
NodeId id_,
tls::KeyPair& kp_,
tls::KeyPairBase& kp_,
size_t sig_tx_interval_ = 0,
size_t sig_ms_interval_ = 0,
bool signature_timer = true) :
@ -670,11 +670,8 @@ namespace ccf
tls::VerifierPtr from_cert = tls::make_verifier(ni.value().cert);
crypto::Sha256Hash root = replicated_state_tree.get_root();
log_hash(root, VERIFY);
bool result = from_cert->verify_hash(
root.h.data(),
root.h.size(),
sig_value.sig.data(),
sig_value.sig.size());
bool result =
from_cert->verify_hash(root.h, sig_value.sig, MDType::SHA256);
if (!result)
{

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

@ -115,9 +115,8 @@ namespace ccf
http::headers::CONTENT_TYPE, http::headervalues::contenttype::JSON);
request.set_body(&body);
crypto::Sha256Hash hash;
const auto contents = node_cert.contents();
tls::do_hash(contents.data(), contents.size(), hash.h, MBEDTLS_MD_SHA256);
crypto::Sha256Hash hash({contents.data(), contents.size()});
const std::string key_id = fmt::format("{:02x}", fmt::join(hash.h, ""));
http::sign_request(request, node_sign_kp, key_id);

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

@ -119,8 +119,9 @@ namespace ccf
static constexpr NodeId invalid_node_id = -1;
NodeId self = invalid_node_id;
tls::KeyPairPtr node_sign_kp;
tls::KeyPairPtr node_encrypt_kp;
std::shared_ptr<tls::KeyPair_mbedTLS> node_encrypt_kp;
tls::Pem node_cert;
tls::CurveID curve_id;
QuoteInfo quote_info;
CodeDigest node_code_id;
#ifdef GET_QUOTE
@ -236,11 +237,12 @@ namespace ccf
ringbuffer::AbstractWriterFactory& writer_factory,
NetworkState& network,
std::shared_ptr<enclave::RPCSessions> rpcsessions,
ShareManager& share_manager) :
ShareManager& share_manager,
const CurveID& curve_id) :
sm(State::uninitialized),
self(INVALID_ID),
node_sign_kp(tls::make_key_pair()),
node_encrypt_kp(tls::make_key_pair()),
node_sign_kp(tls::make_key_pair(curve_id)),
node_encrypt_kp(std::make_shared<tls::KeyPair_mbedTLS>(curve_id)),
writer_factory(writer_factory),
to_host(writer_factory.create_writer_to_outside()),
network(network),
@ -298,6 +300,8 @@ namespace ccf
create_node_cert(config);
open_frontend(ActorsType::nodes);
curve_id = config.curve_id;
#ifdef GET_QUOTE
quote_info = enclave_attestation_provider.generate_quote(
node_sign_kp->public_key_pem());
@ -1354,13 +1358,11 @@ namespace ccf
{
// Accept TLS connections, presenting node certificate signed by network
// certificate
auto nw = tls::make_key_pair({network.identity->priv_key});
auto nw = tls::make_key_pair(network.identity->priv_key);
auto csr = node_sign_kp->create_csr(config.subject_name);
auto sans = get_subject_alternative_names(config);
auto endorsed_node_cert = nw->sign_csr(
node_sign_kp->create_csr(config.subject_name),
fmt::format("CN={}", "CCF Network"),
sans);
auto endorsed_node_cert = nw->sign_csr(network.identity->cert, csr, sans);
rpcsessions->set_cert(
endorsed_node_cert, node_sign_kp->private_key_pem());
@ -1414,9 +1416,8 @@ namespace ccf
request.set_body(&body);
crypto::Sha256Hash hash;
const auto contents = node_cert.contents();
tls::do_hash(contents.data(), contents.size(), hash.h, MBEDTLS_MD_SHA256);
crypto::Sha256Hash hash({contents.data(), contents.size()});
const std::string key_id = fmt::format("{:02x}", fmt::join(hash.h, ""));
http::sign_request(request, node_sign_kp, key_id);
@ -1556,7 +1557,7 @@ namespace ccf
{
auto plain_ledger_secret = LedgerSecretsBroadcast::decrypt(
node_encrypt_kp,
tls::make_public_key(
std::make_shared<PublicKey_mbedTLS>(
ledger_secret_set.primary_public_encryption_key),
encrypted_ledger_secret.encrypted_secret);

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

@ -555,11 +555,7 @@ namespace ccf
void hash_data(Nonce& data, crypto::Sha256Hash& hash)
{
tls::do_hash(
reinterpret_cast<const uint8_t*>(&data),
data.h.size(),
hash.h,
MBEDTLS_MD_SHA256);
hash = crypto::Sha256Hash({data.h.data(), data.h.size()});
}
kv::Consensus::SeqNo get_highest_committed_nonce()

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

@ -3,8 +3,8 @@
#pragma once
#include "backup_signatures.h"
#include "consensus/aft/revealed_nonces.h"
#include "crypto/hash.h"
#include "node_signature.h"
#include "tls/hash.h"
#include "tls/tls.h"
#include "tls/verifier.h"
#include "view_change.h"
@ -83,7 +83,8 @@ namespace ccf
class ProgressTrackerStoreAdapter : public ProgressTrackerStore
{
public:
ProgressTrackerStoreAdapter(kv::AbstractStore& store_, tls::KeyPair& kp_) :
ProgressTrackerStoreAdapter(
kv::AbstractStore& store_, tls::KeyPairBase& kp_) :
store(store_),
kp(kp_),
nodes(ccf::Tables::NODES),
@ -183,7 +184,7 @@ namespace ccf
}
tls::VerifierPtr from_cert = tls::make_verifier(ni.value().cert);
return from_cert->verify_hash(
root.h.data(), root.h.size(), sig, sig_size);
root.h.data(), root.h.size(), sig, sig_size, MDType::SHA256);
}
void sign_view_change_request(
@ -213,11 +214,7 @@ namespace ccf
return false;
}
tls::VerifierPtr from_cert = tls::make_verifier(ni.value().cert);
return from_cert->verify_hash(
h.h.data(),
h.h.size(),
view_change.signature.data(),
view_change.signature.size());
return from_cert->verify_hash(h.h, view_change.signature, MDType::SHA256);
}
bool verify_view_change_request_confirmation(
@ -234,11 +231,7 @@ namespace ccf
}
tls::VerifierPtr from_cert = tls::make_verifier(ni.value().cert);
auto h = hash_new_view(new_view);
return from_cert->verify_hash(
h.h.data(),
h.h.size(),
new_view.signature.data(),
new_view.signature.size());
return from_cert->verify_hash(h.h, new_view.signature, MDType::SHA256);
}
kv::Consensus::SeqNo write_view_change_confirmation(
@ -269,7 +262,7 @@ namespace ccf
crypto::Sha256Hash hash_new_view(ccf::ViewChangeConfirmation& new_view)
{
crypto::CSha256Hash ch;
crypto::ISha256Hash ch;
ch.update(new_view.view);
ch.update(new_view.seqno);
@ -278,12 +271,12 @@ namespace ccf
ch.update(it.second.signature);
}
return ch.finalize();
return ch.finalise();
}
private:
kv::AbstractStore& store;
tls::KeyPair& kp;
tls::KeyPairBase& kp;
ccf::Nodes nodes;
ccf::BackupSignaturesMap backup_signatures;
aft::RevealedNoncesMap revealed_nonces;
@ -294,7 +287,7 @@ namespace ccf
kv::Consensus::View view,
kv::Consensus::SeqNo seqno) const
{
crypto::CSha256Hash ch;
crypto::ISha256Hash ch;
ch.update(view);
ch.update(seqno);
@ -304,7 +297,7 @@ namespace ccf
ch.update(s.sig);
}
return ch.finalize();
return ch.finalise();
}
};

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

@ -98,7 +98,7 @@ namespace ccf
const GetCallerId::In in = params;
auto certs = args.tx.template ro<CertDERs>(certs_table_name);
std::vector<uint8_t> pem(in.cert.begin(), in.cert.end());
std::vector<uint8_t> der = tls::make_verifier(pem)->der_cert_data();
std::vector<uint8_t> der = tls::make_verifier(pem)->cert_der();
auto caller_id_opt = certs->get(der);
if (!caller_id_opt.has_value())

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

@ -107,7 +107,7 @@ namespace ccf
auto size_ = raw_request.size();
MessageHash msg(ForwardedMsg::request_hash, self);
tls::do_hash(data_, size_, msg.hash.h, MBEDTLS_MD_SHA256);
msg.hash = crypto::Sha256Hash({data_, size_});
for (auto to : nodes)
{

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

@ -407,9 +407,8 @@ http::Request create_signed_request(
s.set_body(body);
crypto::Sha256Hash hash;
const auto contents = caller_cert.contents();
tls::do_hash(contents.data(), contents.size(), hash.h, MBEDTLS_MD_SHA256);
crypto::Sha256Hash hash({contents.data(), contents.size()});
const std::string key_id = fmt::format("{:02x}", fmt::join(hash.h, ""));
http::sign_request(s, kp, key_id);
@ -436,17 +435,17 @@ nlohmann::json parse_response_body(
// callers used throughout
auto user_caller = kp -> self_sign("CN=name");
auto user_caller_der = tls::make_verifier(user_caller) -> der_cert_data();
auto user_caller_der = tls::make_verifier(user_caller) -> cert_der();
auto member_caller = kp -> self_sign("CN=name_member");
auto member_caller_der = tls::make_verifier(member_caller) -> der_cert_data();
auto member_caller_der = tls::make_verifier(member_caller) -> cert_der();
auto node_caller = kp -> self_sign("CN=node");
auto node_caller_der = tls::make_verifier(node_caller) -> der_cert_data();
auto node_caller_der = tls::make_verifier(node_caller) -> cert_der();
auto kp_other = tls::make_key_pair();
auto invalid_caller = kp_other -> self_sign("CN=name");
auto invalid_caller_der = tls::make_verifier(invalid_caller) -> der_cert_data();
auto invalid_caller_der = tls::make_verifier(invalid_caller) -> cert_der();
auto anonymous_caller_der = std::vector<uint8_t>();

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

@ -34,7 +34,7 @@ using TResponse = http::SimpleResponseProcessor::Response;
auto kp = tls::make_key_pair();
auto member_cert = kp -> self_sign("CN=name_member");
auto verifier_mem = tls::make_verifier(member_cert);
auto member_caller = verifier_mem -> der_cert_data();
auto member_caller = verifier_mem -> cert_der();
auto user_cert = kp -> self_sign("CN=name_user");
auto dummy_enc_pubk = tls::make_rsa_key_pair() -> public_key_pem();
@ -119,9 +119,8 @@ std::vector<uint8_t> create_signed_request(
serdes::pack(params, default_pack);
r.set_body(&body);
crypto::Sha256Hash hash;
const auto contents = caller.contents();
tls::do_hash(contents.data(), contents.size(), hash.h, MBEDTLS_MD_SHA256);
crypto::Sha256Hash hash({contents.data(), contents.size()});
const std::string key_id = fmt::format("{:02x}", fmt::join(hash.h, ""));
http::sign_request(r, kp_, key_id);
@ -155,7 +154,7 @@ auto frontend_process(
const tls::Pem& caller)
{
auto session = std::make_shared<enclave::SessionContext>(
enclave::InvalidSessionId, tls::make_verifier(caller)->der_cert_data());
enclave::InvalidSessionId, tls::make_verifier(caller)->cert_der());
auto rpc_ctx = enclave::make_rpc_context(session, serialized_request);
http::extract_actor(*rpc_ctx);
auto serialized_response = frontend.process(rpc_ctx);

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

@ -1116,7 +1116,7 @@ DOCTEST_TEST_CASE("Add and remove user via proposed calls")
const auto uid = tx1.rw(network.values)->get(ValueIds::NEXT_USER_ID);
DOCTEST_CHECK(uid);
DOCTEST_CHECK(*uid == 1);
user_der = tls::make_verifier(user_cert)->der_cert_data();
user_der = tls::make_verifier(user_cert)->cert_der();
const auto uid1 = tx1.rw(network.user_certs)->get(user_der);
DOCTEST_CHECK(uid1);
DOCTEST_CHECK(*uid1 == 0);
@ -1224,9 +1224,7 @@ DOCTEST_TEST_CASE(
const auto propose =
create_signed_request(proposal, "proposals", kp, members[proposer_id]);
const auto r = parse_response_body<Propose::Out>(frontend_process(
frontend,
propose,
tls::make_verifier(members[proposer_id])->der_cert_data()));
frontend, propose, tls::make_verifier(members[proposer_id])->cert_der()));
DOCTEST_CHECK(r.state == ProposalState::OPEN);

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

@ -184,7 +184,7 @@ TEST_CASE("Add a node to an opening service")
{
tls::KeyPairPtr kp = tls::make_key_pair();
auto v = tls::make_verifier(kp->self_sign(fmt::format("CN=nodes")));
const auto caller = v->der_cert_data();
const auto caller = v->cert_der();
// Network node info is empty (same as before)
JoinNetworkNodeToNode::In join_input;
@ -260,7 +260,7 @@ TEST_CASE("Add a node to an open service")
{
tls::KeyPairPtr kp = tls::make_key_pair();
auto v = tls::make_verifier(kp->self_sign(fmt::format("CN=nodes")));
const auto caller = v->der_cert_data();
const auto caller = v->cert_der();
// Network node info is empty (same as before)
JoinNetworkNodeToNode::In join_input;

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

@ -16,8 +16,8 @@ namespace ccf
{
private:
static std::vector<uint8_t> encrypt_ledger_secret(
tls::KeyPairPtr encryption_key,
tls::PublicKeyPtr backup_pubk,
std::shared_ptr<tls::KeyPair_mbedTLS> encryption_key,
std::shared_ptr<tls::PublicKey_mbedTLS> backup_pubk,
std::vector<uint8_t>&& plain)
{
// Encrypt secrets with a shared secret derived from backup public
@ -39,7 +39,7 @@ namespace ccf
public:
static void broadcast_some(
NetworkState& network,
tls::KeyPairPtr encryption_key,
std::shared_ptr<tls::KeyPair_mbedTLS> encryption_key,
NodeId self,
kv::Tx& tx,
const LedgerSecretsMap& some_ledger_secrets)
@ -59,7 +59,7 @@ namespace ccf
{s.first,
encrypt_ledger_secret(
encryption_key,
tls::make_public_key(ni.encryption_pub_key),
std::make_shared<tls::PublicKey_mbedTLS>(ni.encryption_pub_key),
std::move(s.second.raw_key))});
}
@ -72,7 +72,7 @@ namespace ccf
static void broadcast_new(
NetworkState& network,
tls::KeyPairPtr encryption_key,
std::shared_ptr<tls::KeyPair_mbedTLS> encryption_key,
kv::Tx& tx,
LedgerSecret&& new_ledger_secret)
{
@ -86,7 +86,7 @@ namespace ccf
{std::nullopt,
encrypt_ledger_secret(
encryption_key,
tls::make_public_key(ni.encryption_pub_key),
std::make_shared<tls::PublicKey_mbedTLS>(ni.encryption_pub_key),
std::move(new_ledger_secret.raw_key))});
secrets->put(
@ -97,8 +97,8 @@ namespace ccf
}
static std::vector<uint8_t> decrypt(
tls::KeyPairPtr encryption_key,
tls::PublicKeyPtr primary_pubk,
std::shared_ptr<tls::KeyPair_mbedTLS> encryption_key,
std::shared_ptr<tls::PublicKey_mbedTLS> primary_pubk,
const std::vector<uint8_t>& cipher)
{
crypto::GcmCipher gcmcipher;

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

@ -75,7 +75,7 @@ TEST_CASE("StateCache")
// Make history to produce signatures
const auto node_id = 0;
auto kp = tls::make_key_pair();
auto kp = make_key_pair();
auto history = std::make_shared<ccf::MerkleTxHistory>(store, node_id, *kp);
store.set_history(history);

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

@ -71,7 +71,7 @@ TEST_CASE("Check signature verification")
ccf::Nodes nodes(ccf::Tables::NODES);
ccf::Signatures signatures(ccf::Tables::SIGNATURES);
auto kp = tls::make_key_pair();
auto kp = make_key_pair();
std::shared_ptr<kv::Consensus> consensus =
std::make_shared<DummyConsensus>(&backup_store);

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

@ -304,9 +304,7 @@ namespace client
key = tls::Pem(raw_key);
crypto::Sha256Hash hash;
tls::do_hash(
raw_cert.data(), raw_cert.size(), hash.h, MBEDTLS_MD_SHA256);
crypto::Sha256Hash hash({raw_cert.data(), raw_cert.size()});
key_id = fmt::format("{:02x}", fmt::join(hash.h, ""));
tls_cert = std::make_shared<tls::Cert>(

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

@ -2,76 +2,62 @@
// Licensed under the Apache 2.0 License.
#pragma once
#include "crypto/hash.h"
#include "ds/logger.h"
#include "tls.h"
#include <secp256k1/include/secp256k1.h>
#include <secp256k1/include/secp256k1_recovery.h>
#include <mbedtls/ecp.h>
#include <openssl/evp.h>
#include <stdexcept>
#include <string>
using namespace crypto;
namespace tls
{
// SNIPPET_START: supported_curves
enum class CurveImpl
enum class CurveID
{
secp384r1 = 1,
secp256k1_mbedtls = 2,
secp256k1_bitcoin = 3,
service_identity_curve_choice = secp384r1,
NONE = 0,
SECP384R1,
SECP256R1
};
static constexpr CurveID service_identity_curve_choice = CurveID::SECP384R1;
// SNIPPET_END: supported_curves
// 2 implementations of secp256k1 are available - mbedtls and bitcoin. Either
// can be asked for explicitly via the CurveImpl enum. For cases where we
// receive a raw 256k1 key/signature/cert only, this flag determines which
// implementation is used
static constexpr bool prefer_bitcoin_secp256k1 = true;
// Helper to access elliptic curve id from context
inline mbedtls_ecp_group_id get_ec_from_context(const mbedtls_pk_context& ctx)
inline mbedtls_ecp_group_id get_mbedtls_ec_from_context(
const mbedtls_pk_context& ctx)
{
return mbedtls_pk_ec(ctx)->grp.id;
}
// Get mbedtls elliptic curve for given CCF curve implementation
inline mbedtls_ecp_group_id get_ec_for_curve_impl(CurveImpl curve)
// Get message digest algorithm to use for given elliptic curve
inline MDType get_md_for_ec(CurveID ec)
{
switch (curve)
switch (ec)
{
case CurveImpl::secp384r1:
{
return MBEDTLS_ECP_DP_SECP384R1;
}
case CurveImpl::secp256k1_mbedtls:
case CurveImpl::secp256k1_bitcoin:
{
return MBEDTLS_ECP_DP_SECP256K1;
}
case CurveID::SECP384R1:
return MDType::SHA384;
case CurveID::SECP256R1:
return MDType::SHA256;
default:
{
throw std::logic_error(
"Unhandled curve type: " +
std::to_string(static_cast<size_t>(curve)));
throw std::logic_error(fmt::format("Unhandled CurveID: {}", ec));
}
}
}
// Get message digest algorithm to use for given elliptic curve
inline mbedtls_md_type_t get_md_for_ec(
inline mbedtls_md_type_t get_mbedtls_md_for_ec(
mbedtls_ecp_group_id ec, bool allow_none = false)
{
switch (ec)
{
case MBEDTLS_ECP_DP_SECP384R1:
{
return MBEDTLS_MD_SHA384;
}
case MBEDTLS_ECP_DP_SECP256K1:
{
case MBEDTLS_ECP_DP_SECP256R1:
return MBEDTLS_MD_SHA256;
}
default:
{
if (allow_none)
@ -80,75 +66,10 @@ namespace tls
}
else
{
const auto curve_info = mbedtls_ecp_curve_info_from_grp_id(ec);
const auto error = fmt::format(
"Unhandled ecp group id: {}",
curve_info ? curve_info->name :
fmt::format("UNKNOWN ({})", (size_t)ec));
const auto error = fmt::format("Unhandled ecp group id: {}", ec);
throw std::logic_error(error);
}
}
}
}
inline bool verify_secp256k_bc(
secp256k1_context* ctx,
const uint8_t* signature,
size_t signature_size,
const uint8_t* hash,
size_t hash_size,
const secp256k1_pubkey* public_key)
{
if (hash_size != 32)
return false;
secp256k1_ecdsa_signature sig;
if (
secp256k1_ecdsa_signature_parse_der(
ctx, &sig, signature, signature_size) != 1)
return false;
secp256k1_ecdsa_signature norm_sig;
if (secp256k1_ecdsa_signature_normalize(ctx, &norm_sig, &sig) == 1)
{
LOG_TRACE_FMT("secp256k1 normalized a signature to lower-S form");
}
return secp256k1_ecdsa_verify(ctx, &norm_sig, hash, public_key) == 1;
}
static void secp256k1_illegal_callback(const char* str, void*)
{
throw std::logic_error(
fmt::format("[libsecp256k1] illegal argument: {}", str));
}
// Wrap calls to secp256k1_context_create, setting illegal callback to throw
// catchable errors rather than aborting, and ensuring destroy is called when
// this goes out of scope
class BCk1Context
{
public:
secp256k1_context* p = nullptr;
BCk1Context(unsigned int flags)
{
p = secp256k1_context_create(flags);
secp256k1_context_set_illegal_callback(
p, secp256k1_illegal_callback, nullptr);
}
~BCk1Context()
{
secp256k1_context_destroy(p);
}
};
using BCk1ContextPtr = std::unique_ptr<BCk1Context>;
inline BCk1ContextPtr make_bc_context(unsigned int flags)
{
return std::make_unique<BCk1Context>(flags);
}
}

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

@ -1,92 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the Apache 2.0 License.
#pragma once
#include "curve.h"
#include <vector>
namespace tls
{
using HashBytes = std::vector<uint8_t>;
template <size_t T>
using HashFixedSize = std::array<uint8_t, T>;
/**
* Hash the given data, with the specified digest algorithm
*
* @return 0 on success
*/
inline int do_hash(
const uint8_t* data_ptr,
size_t data_size,
HashBytes& o_hash,
mbedtls_md_type_t md_type)
{
const auto md_info = mbedtls_md_info_from_type(md_type);
const auto hash_size = mbedtls_md_get_size(md_info);
if (o_hash.size() < hash_size)
{
o_hash.resize(hash_size);
}
return mbedtls_md(md_info, data_ptr, data_size, o_hash.data());
}
/**
* Hash the given data, with the specified digest algorithm
*
* @return 0 on success
*/
template <size_t T>
inline int do_hash(
const uint8_t* data_ptr,
size_t data_size,
HashFixedSize<T>& o_hash,
mbedtls_md_type_t md_type)
{
const auto md_info = mbedtls_md_info_from_type(md_type);
const auto hash_size = mbedtls_md_get_size(md_info);
if (o_hash.size() != hash_size)
{
return -1;
}
return mbedtls_md(md_info, data_ptr, data_size, o_hash.data());
}
/**
* Hash the given data, with an algorithm chosen by key type
*
* @return 0 on success
*/
inline int do_hash(
const mbedtls_pk_context& ctx,
const uint8_t* data_ptr,
size_t data_size,
HashBytes& o_hash,
mbedtls_md_type_t md_type_ = MBEDTLS_MD_NONE)
{
const auto ec = get_ec_from_context(ctx);
mbedtls_md_type_t md_type;
if (md_type_ != MBEDTLS_MD_NONE)
{
md_type = md_type_;
}
else
{
md_type = get_md_for_ec(ec);
}
const auto md_info = mbedtls_md_info_from_type(md_type);
const auto hash_size = mbedtls_md_get_size(md_info);
if (o_hash.size() < hash_size)
o_hash.resize(hash_size);
return mbedtls_md(md_info, data_ptr, data_size, o_hash.data());
}
}

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

@ -57,7 +57,9 @@ namespace tls
ctx = std::move(tmp_ctx);
}
KeyExchangeContext(KeyPairPtr own_kp, PublicKeyPtr peer_pubk) :
KeyExchangeContext(
std::shared_ptr<KeyPair_mbedTLS> own_kp,
std::shared_ptr<PublicKey_mbedTLS> peer_pubk) :
entropy(create_entropy())
{
auto tmp_ctx = mbedtls::make_unique<mbedtls::ECDHContext>();

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

@ -6,818 +6,69 @@
#include "curve.h"
#include "entropy.h"
#include "error_string.h"
#include "hash.h"
#include "mbedtls_wrappers.h"
#include "key_pair_base.h"
#include "key_pair_mbedtls.h"
#include "key_pair_openssl.h"
#include "pem.h"
#include "san.h"
#include <cstring>
#include <iomanip>
#include <limits>
#include <mbedtls/bignum.h>
#include <mbedtls/pem.h>
#include <memory>
namespace tls
{
static constexpr size_t ecp_num_size = 100;
static constexpr size_t max_pem_key_size = 2048;
inline void parse_secp256k_bc(
const mbedtls_pk_context& ctx,
secp256k1_context* bc_ctx,
secp256k1_pubkey* bc_pub)
{
auto k = mbedtls_pk_ec(ctx);
size_t pub_len;
uint8_t pub_buf[ecp_num_size];
int rc = mbedtls_ecp_point_write_binary(
&k->grp,
&k->Q,
MBEDTLS_ECP_PF_COMPRESSED,
&pub_len,
pub_buf,
ecp_num_size);
if (rc != 0)
{
throw std::logic_error(
"mbedtls_ecp_point_write_binary failed: " + error_string(rc));
}
rc = secp256k1_ec_pubkey_parse(bc_ctx, bc_pub, pub_buf, pub_len);
if (rc != 1)
{
throw std::logic_error("secp256k1_ec_pubkey_parse failed");
}
}
struct RecoverableSignature
{
// Signature consists of 32 byte R, 32 byte S, and recovery id. Some formats
// concatenate all 3 into 65 bytes. We stick with libsecp256k1 and separate
// 64 bytes of (R, S) from recovery_id.
static constexpr size_t RS_Size = 64;
std::array<uint8_t, RS_Size> raw;
int recovery_id;
};
class PublicKey
{
protected:
mbedtls::PKContext ctx = mbedtls::make_unique<mbedtls::PKContext>();
PublicKey() {}
public:
/**
* Construct from a pre-initialised pk context
*/
PublicKey(mbedtls::PKContext&& c) : ctx(std::move(c)) {}
virtual ~PublicKey() = default;
/**
* Verify that a signature was produced on contents with the private key
* associated with the public key held by the object.
*
* @param contents Sequence of bytes that was signed
* @param signature Signature as a sequence of bytes
*
* @return Whether the signature matches the contents and the key
*/
bool verify(
const std::vector<uint8_t>& contents,
const std::vector<uint8_t>& signature)
{
return verify(
contents.data(), contents.size(), signature.data(), signature.size());
}
/**
* Verify that a signature was produced on contents with the private key
* associated with the public key held by the object.
*
* @param contents address of contents
* @param contents_size size of contents
* @param sig address of signature
* @param sig_size size of signature
* @param md_type Digest algorithm to use. Derived from the
* public key if MBEDTLS_MD_NONE.
*
* @return Whether the signature matches the contents and the key
*/
bool verify(
const uint8_t* contents,
size_t contents_size,
const uint8_t* sig,
size_t sig_size,
mbedtls_md_type_t md_type = MBEDTLS_MD_NONE)
{
HashBytes hash;
do_hash(*ctx, contents, contents_size, hash, md_type);
return verify_hash(hash.data(), hash.size(), sig, sig_size);
}
/**
* Verify that a signature was produced on a hash with the private key
* associated with the public key held by the object.
*
* @param hash Hash produced from contents as a sequence of bytes
* @param signature Signature as a sequence of bytes
*
* @return Whether the signature matches the hash and the key
*/
bool verify_hash(
const std::vector<uint8_t>& hash, const std::vector<uint8_t>& signature)
{
return verify_hash(
hash.data(), hash.size(), signature.data(), signature.size());
}
virtual bool verify_hash(
const uint8_t* hash,
size_t hash_size,
const uint8_t* sig,
size_t sig_size)
{
const auto md_type = get_md_for_ec(get_ec_from_context(*ctx));
int rc =
mbedtls_pk_verify(ctx.get(), md_type, hash, hash_size, sig, sig_size);
if (rc)
LOG_DEBUG_FMT("Failed to verify signature: {}", error_string(rc));
return rc == 0;
}
/**
* Get the public key in PEM format
*/
Pem public_key_pem()
{
uint8_t data[max_pem_key_size];
int rc = mbedtls_pk_write_pubkey_pem(ctx.get(), data, max_pem_key_size);
if (rc != 0)
{
throw std::logic_error(
"mbedtls_pk_write_pubkey_pem: " + error_string(rc));
}
const size_t len = strlen((char const*)data);
return Pem(data, len);
}
mbedtls_pk_context* get_raw_context() const
{
return ctx.get();
}
};
class PublicKey_k1Bitcoin : public PublicKey
{
protected:
BCk1ContextPtr bc_ctx = make_bc_context(SECP256K1_CONTEXT_VERIFY);
secp256k1_pubkey bc_pub;
public:
template <typename... Ts>
PublicKey_k1Bitcoin(Ts... ts) : PublicKey(std::forward<Ts>(ts)...)
{
parse_secp256k_bc(*ctx, bc_ctx->p, &bc_pub);
}
bool verify_hash(
const uint8_t* hash,
size_t hash_size,
const uint8_t* sig,
size_t sig_size) override
{
return verify_secp256k_bc(
bc_ctx->p, sig, sig_size, hash, hash_size, &bc_pub);
}
static PublicKey_k1Bitcoin recover_key(
const RecoverableSignature& rs, CBuffer hashed)
{
int rc;
size_t buf_len = 65;
std::array<uint8_t, 65> buf;
if (hashed.n != 32)
{
throw std::logic_error(
fmt::format("Expected {} bytes in hash, got {}", 32, hashed.n));
}
// Recover with libsecp256k1
{
auto ctx = make_bc_context(SECP256K1_CONTEXT_VERIFY);
secp256k1_ecdsa_recoverable_signature sig;
rc = secp256k1_ecdsa_recoverable_signature_parse_compact(
ctx->p, &sig, rs.raw.data(), rs.recovery_id);
if (rc != 1)
{
throw std::logic_error(
"secp256k1_ecdsa_recoverable_signature_parse_compact failed");
}
secp256k1_pubkey pubk;
rc = secp256k1_ecdsa_recover(ctx->p, &pubk, &sig, hashed.p);
if (rc != 1)
{
throw std::logic_error("secp256k1_ecdsa_recover failed");
}
rc = secp256k1_ec_pubkey_serialize(
ctx->p, buf.data(), &buf_len, &pubk, SECP256K1_EC_UNCOMPRESSED);
if (rc != 1)
{
throw std::logic_error("secp256k1_ec_pubkey_serialize failed");
}
}
// Read recovered key into mbedtls context
{
auto pk_info = mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY);
if (pk_info == nullptr)
{
throw std::logic_error("mbedtls_pk_info_t not found");
}
auto ctx = mbedtls::make_unique<mbedtls::PKContext>();
rc = mbedtls_pk_setup(ctx.get(), pk_info);
if (rc != 0)
{
throw std::logic_error(
"mbedtls_pk_setup failed with: " + error_string(rc));
}
auto kp = mbedtls_pk_ec(*ctx);
rc = mbedtls_ecp_group_load(&kp->grp, MBEDTLS_ECP_DP_SECP256K1);
if (rc != 0)
{
throw std::logic_error(
"mbedtls_ecp_group_load failed with: " + error_string(rc));
}
rc = mbedtls_ecp_point_read_binary(
&kp->grp, &kp->Q, buf.data(), buf.size());
if (rc != 0)
{
throw std::logic_error(
"mbedtls_ecp_point_read_binary failed with: " + error_string(rc));
}
rc = mbedtls_ecp_check_pubkey(&kp->grp, &kp->Q);
if (rc != 0)
{
throw std::logic_error(
"mbedtls_ecp_check_pubkey failed with: " + error_string(rc));
}
return PublicKey_k1Bitcoin(std::move(ctx));
}
}
};
using PublicKeyPtr = std::shared_ptr<PublicKey>;
#ifdef CRYPTO_PROVIDER_IS_MBEDTLS
using PublicKey = PublicKey_mbedTLS;
#else
using PublicKey = PublicKey_OpenSSL;
#endif
using PublicKeyPtr = std::shared_ptr<PublicKeyBase>;
/**
* Construct PublicKey from a raw public key in PEM format
*
* @param public_pem Sequence of bytes containing the key in PEM format
* @param use_bitcoin_impl If true, and the key is on secp256k1, then the
* bitcoin secp256k1 library will be used as the implementation rather than
* mbedtls
*/
inline PublicKeyPtr make_public_key(
const Pem& public_pem, bool use_bitcoin_impl = prefer_bitcoin_secp256k1)
inline PublicKeyPtr make_public_key(const Pem& public_pem)
{
auto ctx = mbedtls::make_unique<mbedtls::PKContext>();
int rc = mbedtls_pk_parse_public_key(
ctx.get(), public_pem.data(), public_pem.size());
if (rc != 0)
{
throw std::logic_error(fmt::format(
"Could not parse public key PEM: {}\n\n(Key: {})",
error_string(rc),
public_pem.str()));
}
const auto curve = get_ec_from_context(*ctx);
if (curve == MBEDTLS_ECP_DP_SECP256K1 && use_bitcoin_impl)
{
return std::make_shared<PublicKey_k1Bitcoin>(std::move(ctx));
}
else
{
return std::make_shared<PublicKey>(std::move(ctx));
}
return std::make_shared<PublicKey>(public_pem);
}
/**
* Construct PublicKey from a raw public key in DER format
*
* @param public_der Sequence of bytes containing the key in DER format
* @param use_bitcoin_impl If true, and the key is on secp256k1, then the
* bitcoin secp256k1 library will be used as the implementation rather than
* mbedtls
*/
inline PublicKeyPtr make_public_key(
const std::vector<uint8_t> public_der,
bool use_bitcoin_impl = prefer_bitcoin_secp256k1)
inline PublicKeyPtr make_public_key(const std::vector<uint8_t> public_der)
{
auto ctx = mbedtls::make_unique<mbedtls::PKContext>();
int rc = mbedtls_pk_parse_public_key(
ctx.get(), public_der.data(), public_der.size());
if (rc != 0)
{
throw std::logic_error(
fmt::format("Could not parse public key DER: {}", error_string(rc)));
}
const auto curve = get_ec_from_context(*ctx);
if (curve == MBEDTLS_ECP_DP_SECP256K1 && use_bitcoin_impl)
{
return std::make_shared<PublicKey_k1Bitcoin>(std::move(ctx));
}
else
{
return std::make_shared<PublicKey>(std::move(ctx));
}
return std::make_shared<PublicKey>(public_der);
}
class KeyPair : public PublicKey
{
public:
/**
* Create a new public / private ECDSA key pair
*/
KeyPair(mbedtls_ecp_group_id ec)
{
EntropyPtr entropy = create_entropy();
int rc = mbedtls_pk_setup(
ctx.get(), mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY));
if (rc != 0)
{
throw std::logic_error(
"Could not set up ECDSA context: " + error_string(rc));
}
rc = mbedtls_ecp_gen_key(
ec, mbedtls_pk_ec(*ctx), entropy->get_rng(), entropy->get_data());
if (rc != 0)
{
throw std::logic_error(
"Could not generate ECDSA keypair: " + error_string(rc));
}
const auto actual_ec = get_ec_from_context(*ctx);
if (actual_ec != ec)
{
throw std::logic_error(
"Created key and received unexpected type: " +
std::to_string(actual_ec) + " != " + error_string(ec));
}
}
/**
* Initialise from existing pre-parsed key
*/
KeyPair(mbedtls::PKContext&& k) : PublicKey(std::move(k)) {}
KeyPair(const KeyPair&) = delete;
public:
/**
* Get the private key in PEM format
*/
Pem private_key_pem()
{
uint8_t data[max_pem_key_size];
int rc = mbedtls_pk_write_key_pem(ctx.get(), data, max_pem_key_size);
if (rc != 0)
{
throw std::logic_error("mbedtls_pk_write_key_pem: " + error_string(rc));
}
const size_t len = strlen((char const*)data);
return Pem(data, len);
}
/**
* Create signature over hash of data from private key.
*
* @param d data
*
* @return Signature as a vector
*/
std::vector<uint8_t> sign(CBuffer d, mbedtls_md_type_t md_type = {}) const
{
HashBytes hash;
do_hash(*ctx, d.p, d.rawSize(), hash, md_type);
return sign_hash(hash.data(), hash.size());
}
/**
* Write signature over hash of data, and the size of that signature to
* specified locations.
*
* Important: sig must point somewhere that's at least
* MBEDTLS_E{C,D}DSA_MAX_LEN.
*
* @param d data
* @param sig_size location to which the signature size will be written.
* Initial value should be max size of sig
* @param sig location to which the signature will be written
*
* @return 0 if successful, error code of mbedtls_pk_sign otherwise,
* or 0xf if the signature_size exceeds that of a uint8_t.
*/
int sign(
CBuffer d,
size_t* sig_size,
uint8_t* sig,
mbedtls_md_type_t md_type = {}) const
{
HashBytes hash;
do_hash(*ctx, d.p, d.rawSize(), hash, md_type);
return sign_hash(hash.data(), hash.size(), sig_size, sig);
}
/**
* Create signature over hashed data.
*
* @param hash First byte in hash sequence
* @param hash_size Number of bytes in hash sequence
*
* @return Signature as a vector
*/
std::vector<uint8_t> sign_hash(const uint8_t* hash, size_t hash_size) const
{
uint8_t sig[MBEDTLS_ECDSA_MAX_LEN];
size_t written = MBEDTLS_ECDSA_MAX_LEN;
if (sign_hash(hash, hash_size, &written, sig) != 0)
{
return {};
}
return {sig, sig + written};
}
virtual int sign_hash(
const uint8_t* hash,
size_t hash_size,
size_t* sig_size,
uint8_t* sig) const
{
EntropyPtr entropy = create_entropy();
const auto ec = get_ec_from_context(*ctx);
const auto md_type = get_md_for_ec(ec, true);
return mbedtls_pk_sign(
ctx.get(),
md_type,
hash,
hash_size,
sig,
sig_size,
entropy->get_rng(),
entropy->get_data());
}
/**
* Create a certificate signing request for this key pair. If we were
* loaded from a private key, there will be no public key available for
* this call.
*/
Pem create_csr(const std::string& name)
{
auto csr = mbedtls::make_unique<mbedtls::X509WriteCsr>();
mbedtls_x509write_csr_set_md_alg(csr.get(), MBEDTLS_MD_SHA512);
if (mbedtls_x509write_csr_set_subject_name(csr.get(), name.c_str()) != 0)
return {};
mbedtls_x509write_csr_set_key(csr.get(), ctx.get());
uint8_t buf[4096];
memset(buf, 0, sizeof(buf));
EntropyPtr entropy = create_entropy();
if (
mbedtls_x509write_csr_pem(
csr.get(),
buf,
sizeof(buf),
entropy->get_rng(),
entropy->get_data()) != 0)
return {};
auto len = strlen((char*)buf);
return Pem(buf, len);
}
Pem sign_csr(
const Pem& pem,
const std::string& issuer,
const std::vector<SubjectAltName> subject_alt_names,
bool ca = false)
{
auto entropy = create_entropy();
auto csr = mbedtls::make_unique<mbedtls::X509Csr>();
auto serial = mbedtls::make_unique<mbedtls::MPI>();
auto crt = mbedtls::make_unique<mbedtls::X509WriteCrt>();
if (mbedtls_x509_csr_parse(csr.get(), pem.data(), pem.size()) != 0)
return {};
char subject[512];
auto r = mbedtls_x509_dn_gets(subject, sizeof(subject), &csr->subject);
if (r < 0)
return {};
mbedtls_x509write_crt_set_md_alg(
crt.get(), get_md_for_ec(get_ec_from_context(*ctx)));
mbedtls_x509write_crt_set_subject_key(crt.get(), &csr->pk);
mbedtls_x509write_crt_set_issuer_key(crt.get(), ctx.get());
if (
mbedtls_mpi_fill_random(
serial.get(), 16, entropy->get_rng(), entropy->get_data()) != 0)
return {};
if (mbedtls_x509write_crt_set_subject_name(crt.get(), subject) != 0)
return {};
if (mbedtls_x509write_crt_set_issuer_name(crt.get(), issuer.c_str()) != 0)
return {};
if (mbedtls_x509write_crt_set_serial(crt.get(), serial.get()) != 0)
return {};
// Note: 825-day validity range
// https://support.apple.com/en-us/HT210176
if (
mbedtls_x509write_crt_set_validity(
crt.get(), "20191101000000", "20211231235959") != 0)
return {};
if (
mbedtls_x509write_crt_set_basic_constraints(crt.get(), ca ? 1 : 0, 0) !=
0)
return {};
if (mbedtls_x509write_crt_set_subject_key_identifier(crt.get()) != 0)
return {};
if (mbedtls_x509write_crt_set_authority_key_identifier(crt.get()) != 0)
return {};
// Because mbedtls does not support parsing x509v3 extensions from a
// CSR (https://github.com/ARMmbed/mbedtls/issues/2912), the CA sets the
// SAN directly instead of reading it from the CSR
try
{
auto rc =
x509write_crt_set_subject_alt_names(crt.get(), subject_alt_names);
if (rc != 0)
{
LOG_FAIL_FMT("Failed to set subject alternative names ({})", rc);
return {};
}
}
catch (const std::logic_error& err)
{
LOG_FAIL_FMT("Error writing SAN: {}", err.what());
return {};
}
uint8_t buf[4096];
memset(buf, 0, sizeof(buf));
if (
mbedtls_x509write_crt_pem(
crt.get(),
buf,
sizeof(buf),
entropy->get_rng(),
entropy->get_data()) != 0)
return {};
auto len = strlen((char*)buf);
return Pem(buf, len);
}
Pem self_sign(
const std::string& name,
const std::optional<SubjectAltName> subject_alt_name = std::nullopt,
bool ca = true)
{
std::vector<SubjectAltName> sans;
if (subject_alt_name.has_value())
sans.push_back(subject_alt_name.value());
auto csr = create_csr(name);
return sign_csr(csr, name, sans, ca);
}
Pem self_sign(
const std::string& name,
const std::vector<SubjectAltName> subject_alt_names,
bool ca = true)
{
auto csr = create_csr(name);
return sign_csr(csr, name, subject_alt_names, ca);
}
};
class KeyPair_k1Bitcoin : public KeyPair
{
protected:
BCk1ContextPtr bc_ctx =
make_bc_context(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN);
secp256k1_pubkey bc_pub;
static constexpr size_t privk_size = 32;
uint8_t c4_priv[privk_size] = {0};
public:
template <typename... Ts>
KeyPair_k1Bitcoin(Ts... ts) : KeyPair(std::forward<Ts>(ts)...)
{
const auto ec = get_ec_from_context(*ctx);
if (ec != MBEDTLS_ECP_DP_SECP256K1)
{
throw std::logic_error(
"Bitcoin implementation cannot extend curve on " +
std::to_string(ec));
}
int rc = 0;
rc = mbedtls_mpi_write_binary(
&(mbedtls_pk_ec(*ctx)->d), c4_priv, privk_size);
if (rc != 0)
{
throw std::logic_error(
"Could not extract raw private key: " + error_string(rc));
}
if (secp256k1_ec_seckey_verify(bc_ctx->p, c4_priv) != 1)
{
throw std::logic_error("secp256k1 private key is not valid");
}
parse_secp256k_bc(*ctx, bc_ctx->p, &bc_pub);
}
// Since this inherits from PublicKey (via Keypair), rather than
// PublicKey_k1Bitcoin, we re-override verify_hash here
bool verify_hash(
const uint8_t* hash,
size_t hash_size,
const uint8_t* signature,
size_t signature_size) override
{
bool ok = verify_secp256k_bc(
bc_ctx->p, signature, signature_size, hash, hash_size, &bc_pub);
return ok;
}
int sign_hash(
const uint8_t* hash,
size_t hash_size,
size_t* sig_size,
uint8_t* sig) const override
{
if (hash_size != 32)
return -1;
secp256k1_ecdsa_signature k1_sig;
if (
secp256k1_ecdsa_sign(
bc_ctx->p, &k1_sig, hash, c4_priv, nullptr, nullptr) != 1)
return -2;
if (
secp256k1_ecdsa_signature_serialize_der(
bc_ctx->p, sig, sig_size, &k1_sig) != 1)
return -3;
return 0;
}
RecoverableSignature sign_recoverable_hashed(CBuffer hashed)
{
int rc;
if (hashed.n != 32)
{
throw std::logic_error(
fmt::format("Expected {} bytes in hash, got {}", 32, hashed.n));
}
secp256k1_ecdsa_recoverable_signature sig;
rc = secp256k1_ecdsa_sign_recoverable(
bc_ctx->p, &sig, hashed.p, c4_priv, nullptr, nullptr);
if (rc != 1)
{
throw std::logic_error("secp256k1_ecdsa_sign_recoverable failed");
}
RecoverableSignature ret;
rc = secp256k1_ecdsa_recoverable_signature_serialize_compact(
bc_ctx->p, ret.raw.data(), &ret.recovery_id, &sig);
if (rc != 1)
{
throw std::logic_error(
"secp256k1_ecdsa_recoverable_signature_serialize_compact failed");
}
return ret;
}
};
using KeyPairPtr = std::shared_ptr<KeyPair>;
inline mbedtls::PKContext parse_private_key(
const Pem& pkey, CBuffer pw = nullb)
{
auto key = mbedtls::make_unique<mbedtls::PKContext>();
// keylen is +1 to include terminating null byte
int rc =
mbedtls_pk_parse_key(key.get(), pkey.data(), pkey.size(), pw.p, pw.n);
if (rc != 0)
{
throw std::logic_error("Could not parse key: " + error_string(rc));
}
return key;
}
#ifdef CRYPTO_PROVIDER_IS_MBEDTLS
using KeyPair = KeyPair_mbedTLS;
#else
using KeyPair = KeyPair_OpenSSL;
#endif
using KeyPairPtr = std::shared_ptr<KeyPairBase>;
/**
* Create a new public / private ECDSA key pair on specified curve and
* implementation
*/
inline KeyPairPtr make_key_pair(
CurveImpl curve = CurveImpl::service_identity_curve_choice)
CurveID curve_id = service_identity_curve_choice)
{
const auto ec = get_ec_for_curve_impl(curve);
if (curve == CurveImpl::secp256k1_bitcoin)
{
return KeyPairPtr(new KeyPair_k1Bitcoin(ec));
}
else
{
return KeyPairPtr(new KeyPair(ec));
}
return std::make_shared<KeyPair>(curve_id);
}
/**
* Create a public / private ECDSA key pair from existing private key data
*/
inline KeyPairPtr make_key_pair(
const Pem& pkey,
CBuffer pw = nullb,
bool use_bitcoin_impl = prefer_bitcoin_secp256k1)
inline KeyPairPtr make_key_pair(const Pem& pkey, CBuffer pw = nullb)
{
auto key = parse_private_key(pkey, pw);
const auto curve = get_ec_from_context(*key);
if (curve == MBEDTLS_ECP_DP_SECP256K1 && use_bitcoin_impl)
{
return std::make_shared<KeyPair_k1Bitcoin>(std::move(key));
}
else
{
return std::make_shared<KeyPair>(std::move(key));
}
return std::make_shared<KeyPair>(pkey, pw);
}
static inline tls::Pem public_key_pem_from_cert(const tls::Pem& cert)

149
src/tls/key_pair_base.h Normal file
Просмотреть файл

@ -0,0 +1,149 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the Apache 2.0 License.
#pragma once
#include "pem.h"
namespace tls
{
static constexpr size_t max_pem_key_size = 2048;
static inline void hexdump(
const char* name, const uint8_t* bytes, size_t size)
{
printf("%s: ", name);
for (size_t i = 0; i < size; i++)
printf("%02x", bytes[i]);
printf("\n");
}
class PublicKeyBase
{
public:
virtual CurveID get_curve_id() const = 0;
virtual bool verify(
const uint8_t* contents,
size_t contents_size,
const uint8_t* sig,
size_t sig_size,
MDType md_type,
HashBytes& bytes) = 0;
/**
* Verify that a signature was produced on contents with the private key
* associated with the public key held by the object.
*
* @param contents address of contents
* @param contents_size size of contents
* @param sig address of signature
* @param sig_size size of signature
* @param md_type Digest algorithm to use. Derived from the public key if
* MDType::None.
*
* @return Whether the signature matches the contents and the key
*/
bool verify(
const uint8_t* contents,
size_t contents_size,
const uint8_t* sig,
size_t sig_size,
MDType md_type = MDType::NONE)
{
HashBytes hash;
return verify(contents, contents_size, sig, sig_size, md_type, hash);
}
/**
* Verify that a signature was produced on contents with the private key
* associated with the public key held by the object.
*
* @param contents Sequence of bytes that was signed
* @param signature Signature as a sequence of bytes
*
* @return Whether the signature matches the contents and the key
*/
virtual bool verify(
const std::vector<uint8_t>& contents,
const std::vector<uint8_t>& signature)
{
return verify(
contents.data(), contents.size(), signature.data(), signature.size());
}
virtual bool verify_hash(
const std::vector<uint8_t>& hash,
const std::vector<uint8_t>& signature,
MDType md_type)
{
return verify_hash(
hash.data(), hash.size(), signature.data(), signature.size(), md_type);
}
virtual bool verify_hash(
const uint8_t* hash,
size_t hash_size,
const uint8_t* sig,
size_t sig_size,
MDType md_type) = 0;
/**
* Get the public key in PEM format
*/
virtual Pem public_key_pem() const = 0;
};
class KeyPairBase
{
public:
virtual ~KeyPairBase() = default;
virtual Pem private_key_pem() const = 0;
virtual Pem public_key_pem() const = 0;
virtual bool verify(
const std::vector<uint8_t>& contents,
const std::vector<uint8_t>& signature) = 0;
virtual std::vector<uint8_t> sign_hash(
const uint8_t* hash, size_t hash_size) const = 0;
virtual int sign_hash(
const uint8_t* hash,
size_t hash_size,
size_t* sig_size,
uint8_t* sig) const = 0;
virtual std::vector<uint8_t> sign(CBuffer d, MDType md_type = {}) const = 0;
virtual Pem create_csr(const std::string& name) const = 0;
virtual Pem sign_csr(
const Pem& issuer_cert,
const Pem& signing_request,
const std::vector<SubjectAltName> subject_alt_names,
bool ca = false) const = 0;
Pem self_sign(
const std::string& name,
const std::optional<SubjectAltName> subject_alt_name = std::nullopt,
bool ca = true) const
{
std::vector<SubjectAltName> sans;
if (subject_alt_name.has_value())
sans.push_back(subject_alt_name.value());
auto csr = create_csr(name);
return sign_csr(Pem(0), csr, sans, ca);
}
Pem self_sign(
const std::string& name,
const std::vector<SubjectAltName> subject_alt_names,
bool ca = true) const
{
auto csr = create_csr(name);
return sign_csr(Pem(0), csr, subject_alt_names, ca);
}
};
}

488
src/tls/key_pair_mbedtls.h Normal file
Просмотреть файл

@ -0,0 +1,488 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the Apache 2.0 License.
#pragma once
#include "key_pair_base.h"
#include "mbedtls_wrappers.h"
#include <mbedtls/bignum.h>
#include <mbedtls/pem.h>
#include <mbedtls/pk.h>
namespace tls
{
class PublicKey_mbedTLS : public PublicKeyBase
{
protected:
mbedtls::PKContext ctx = mbedtls::make_unique<mbedtls::PKContext>();
PublicKey_mbedTLS() {}
inline mbedtls_md_type_t get_md_type(MDType mdt) const
{
switch (mdt)
{
case MDType::NONE:
return MBEDTLS_MD_NONE;
case MDType::SHA1:
return MBEDTLS_MD_SHA1;
case MDType::SHA256:
return MBEDTLS_MD_SHA256;
case MDType::SHA384:
return MBEDTLS_MD_SHA384;
case MDType::SHA512:
return MBEDTLS_MD_SHA512;
default:
return MBEDTLS_MD_NONE;
}
return MBEDTLS_MD_NONE;
}
public:
PublicKey_mbedTLS(PublicKey_mbedTLS&& pk) = default;
/**
* Construct from PEM
*/
PublicKey_mbedTLS(const Pem& pem)
{
int rc = mbedtls_pk_parse_public_key(ctx.get(), pem.data(), pem.size());
if (rc != 0)
{
throw std::logic_error(fmt::format(
"Could not parse public key PEM: {}\n\n(Key: {})",
error_string(rc),
pem.str()));
}
}
/**
* Construct from DER
*/
PublicKey_mbedTLS(const std::vector<uint8_t>& der)
{
int rc = mbedtls_pk_parse_public_key(ctx.get(), der.data(), der.size());
if (rc != 0)
{
throw std::logic_error(
fmt::format("Could not parse public key DER: {}", error_string(rc)));
}
}
virtual CurveID get_curve_id() const override
{
return get_curve_id(ctx.get());
}
static CurveID get_curve_id(const mbedtls_pk_context* pk_ctx)
{
if (mbedtls_pk_can_do(pk_ctx, MBEDTLS_PK_ECKEY))
{
auto grp_id = mbedtls_pk_ec(*pk_ctx)->grp.id;
switch (grp_id)
{
case MBEDTLS_ECP_DP_SECP384R1:
return CurveID::SECP384R1;
case MBEDTLS_ECP_DP_SECP256R1:
return CurveID::SECP256R1;
default:
throw std::logic_error(
fmt::format("unsupported mbedTLS group ID {}", grp_id));
}
}
return CurveID::NONE;
}
/**
* Construct from a pre-initialised pk context
*/
PublicKey_mbedTLS(mbedtls::PKContext&& c) : ctx(std::move(c)) {}
virtual ~PublicKey_mbedTLS() = default;
using PublicKeyBase::verify;
using PublicKeyBase::verify_hash;
virtual bool verify(
const uint8_t* contents,
size_t contents_size,
const uint8_t* sig,
size_t sig_size,
MDType md_type,
HashBytes& bytes) override
{
if (md_type == MDType::NONE)
{
md_type = get_md_for_ec(get_curve_id());
}
MBedHashProvider hp;
bytes = hp.Hash(contents, contents_size, md_type);
return verify_hash(bytes.data(), bytes.size(), sig, sig_size, md_type);
}
virtual bool verify_hash(
const uint8_t* hash,
size_t hash_size,
const uint8_t* sig,
size_t sig_size,
MDType md_type) override
{
if (md_type == MDType::NONE)
{
md_type = get_md_for_ec(get_curve_id());
}
const auto mmdt = get_md_type(md_type);
int rc =
mbedtls_pk_verify(ctx.get(), mmdt, hash, hash_size, sig, sig_size);
if (rc)
LOG_DEBUG_FMT("Failed to verify signature: {}", error_string(rc));
return rc == 0;
}
/**
* Get the public key in PEM format
*/
virtual Pem public_key_pem() const override
{
uint8_t data[max_pem_key_size];
int rc = mbedtls_pk_write_pubkey_pem(ctx.get(), data, max_pem_key_size);
if (rc != 0)
{
throw std::logic_error(
"mbedtls_pk_write_pubkey_pem: " + error_string(rc));
}
const size_t len = strlen((char const*)data);
return Pem(data, len);
}
mbedtls_pk_context* get_raw_context() const
{
return ctx.get();
}
};
class KeyPair_mbedTLS : public PublicKey_mbedTLS, public KeyPairBase
{
public:
inline mbedtls_ecp_group_id get_mbedtls_group_id(CurveID gid)
{
switch (gid)
{
case CurveID::NONE:
return MBEDTLS_ECP_DP_NONE;
case CurveID::SECP384R1:
return MBEDTLS_ECP_DP_SECP384R1;
case CurveID::SECP256R1:
return MBEDTLS_ECP_DP_SECP256R1;
default:
throw std::logic_error(fmt::format("unsupported CurveID {}", gid));
}
return MBEDTLS_ECP_DP_NONE;
}
/**
* Create a new public / private ECDSA key pair
*/
KeyPair_mbedTLS(CurveID cid) : PublicKey_mbedTLS()
{
mbedtls_ecp_group_id ec = get_mbedtls_group_id(cid);
EntropyPtr entropy = create_entropy();
int rc = mbedtls_pk_setup(
ctx.get(), mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY));
if (rc != 0)
{
throw std::logic_error(
"Could not set up ECDSA context: " + error_string(rc));
}
rc = mbedtls_ecp_gen_key(
ec, mbedtls_pk_ec(*ctx), entropy->get_rng(), entropy->get_data());
if (rc != 0)
{
throw std::logic_error(
"Could not generate ECDSA keypair: " + error_string(rc));
}
const auto actual_ec = get_mbedtls_ec_from_context(*ctx);
if (actual_ec != ec)
{
throw std::logic_error(
"Created key and received unexpected type: " +
std::to_string(actual_ec) + " != " + error_string(ec));
}
}
KeyPair_mbedTLS(const Pem& pem, CBuffer pw = nullb)
{
// keylen is +1 to include terminating null byte
int rc =
mbedtls_pk_parse_key(ctx.get(), pem.data(), pem.size(), pw.p, pw.n);
if (rc != 0)
{
throw std::logic_error(
"Could not parse private key: " + error_string(rc));
}
}
/**
* Initialise from existing pre-parsed key
*/
KeyPair_mbedTLS(mbedtls::PKContext&& k) : PublicKey_mbedTLS(std::move(k)) {}
KeyPair_mbedTLS(const KeyPair_mbedTLS&) = delete;
using PublicKey_mbedTLS::verify;
virtual bool verify(
const std::vector<uint8_t>& contents,
const std::vector<uint8_t>& signature) override
{
return PublicKey_mbedTLS::verify(contents, signature);
}
/**
* Get the private key in PEM format
*/
virtual Pem private_key_pem() const override
{
uint8_t data[max_pem_key_size];
int rc = mbedtls_pk_write_key_pem(ctx.get(), data, max_pem_key_size);
if (rc != 0)
{
throw std::logic_error("mbedtls_pk_write_key_pem: " + error_string(rc));
}
const size_t len = strlen((char const*)data);
return Pem(data, len);
}
/**
* Get the public key in PEM format
*/
virtual Pem public_key_pem() const override
{
return PublicKey_mbedTLS::public_key_pem();
}
/**
* Create signature over hash of data from private key.
*
* @param d data
*
* @return Signature as a vector
*/
virtual std::vector<uint8_t> sign(
CBuffer d, MDType md_type = {}) const override
{
if (md_type == MDType::NONE)
{
md_type = get_md_for_ec(get_curve_id());
}
MBedHashProvider hp;
HashBytes hash = hp.Hash(d.p, d.rawSize(), md_type);
return sign_hash(hash.data(), hash.size());
}
/**
* Write signature over hash of data, and the size of that signature to
* specified locations.
*
* Important: sig must point somewhere that's at least
* MBEDTLS_E{C,D}DSA_MAX_LEN.
*
* @param d data
* @param sig_size location to which the signature size will be written.
* Initial value should be max size of sig
* @param sig location to which the signature will be written
*
* @return 0 if successful, error code of mbedtls_pk_sign otherwise,
* or 0xf if the signature_size exceeds that of a uint8_t.
*/
int sign(
CBuffer d, size_t* sig_size, uint8_t* sig, MDType md_type = {}) const
{
if (md_type == MDType::NONE)
{
md_type = get_md_for_ec(get_curve_id());
}
MBedHashProvider hp;
HashBytes hash = hp.Hash(d.p, d.rawSize(), md_type);
return sign_hash(hash.data(), hash.size(), sig_size, sig);
}
/**
* Create signature over hashed data.
*
* @param hash First byte in hash sequence
* @param hash_size Number of bytes in hash sequence
*
* @return Signature as a vector
*/
std::vector<uint8_t> sign_hash(
const uint8_t* hash, size_t hash_size) const override
{
uint8_t sig[MBEDTLS_ECDSA_MAX_LEN];
size_t written = sizeof(sig);
if (sign_hash(hash, hash_size, &written, sig) != 0)
{
return {};
}
return {sig, sig + written};
}
virtual int sign_hash(
const uint8_t* hash,
size_t hash_size,
size_t* sig_size,
uint8_t* sig) const override
{
EntropyPtr entropy = create_entropy();
// mbedTLS wants an MD algorithm; even when it doesn't use it, it stil
// checks that the hash size matches.
const auto mmdt = get_md_type(get_md_for_ec(get_curve_id()));
int r = mbedtls_pk_sign(
ctx.get(),
mmdt,
hash,
hash_size,
sig,
sig_size,
entropy->get_rng(),
entropy->get_data());
return r;
}
/**
* Create a certificate signing request for this key pair. If we were
* loaded from a private key, there will be no public key available for
* this call.
*/
virtual Pem create_csr(const std::string& name) const override
{
auto csr = mbedtls::make_unique<mbedtls::X509WriteCsr>();
mbedtls_x509write_csr_set_md_alg(csr.get(), MBEDTLS_MD_SHA512);
if (mbedtls_x509write_csr_set_subject_name(csr.get(), name.c_str()) != 0)
return {};
mbedtls_x509write_csr_set_key(csr.get(), ctx.get());
uint8_t buf[4096];
memset(buf, 0, sizeof(buf));
EntropyPtr entropy = create_entropy();
if (
mbedtls_x509write_csr_pem(
csr.get(),
buf,
sizeof(buf),
entropy->get_rng(),
entropy->get_data()) != 0)
return {};
auto len = strlen((char*)buf);
return Pem(buf, len);
}
void MCHK(int rc) const
{
if (rc != 0)
{
throw std::logic_error(
fmt::format("mbedTLS error: {}", error_string(rc)));
}
}
virtual Pem sign_csr(
const Pem& issuer_cert,
const Pem& signing_request,
const std::vector<SubjectAltName> subject_alt_names,
bool ca = false) const override
{
auto entropy = create_entropy();
auto csr = mbedtls::make_unique<mbedtls::X509Csr>();
auto serial = mbedtls::make_unique<mbedtls::MPI>();
auto crt = mbedtls::make_unique<mbedtls::X509WriteCrt>();
auto icrt = mbedtls::make_unique<mbedtls::X509Crt>();
MCHK(mbedtls_x509_csr_parse(
csr.get(), signing_request.data(), signing_request.size()));
char subject[512];
mbedtls_x509_dn_gets(subject, sizeof(subject), &csr->subject);
mbedtls_x509write_crt_set_md_alg(
crt.get(), get_mbedtls_md_for_ec(get_mbedtls_ec_from_context(*ctx)));
mbedtls_x509write_crt_set_subject_key(crt.get(), &csr->pk);
if (!issuer_cert.empty())
{
MCHK(mbedtls_x509_crt_parse(
icrt.get(), issuer_cert.data(), issuer_cert.size()));
mbedtls_x509write_crt_set_issuer_key(crt.get(), ctx.get());
char issuer_name[512];
mbedtls_x509_dn_gets(issuer_name, sizeof(issuer_name), &icrt->subject);
MCHK(mbedtls_x509write_crt_set_issuer_name(crt.get(), issuer_name));
}
else
{
mbedtls_x509write_crt_set_issuer_key(crt.get(), ctx.get());
MCHK(mbedtls_x509write_crt_set_issuer_name(crt.get(), subject));
}
MCHK(mbedtls_mpi_fill_random(
serial.get(), 16, entropy->get_rng(), entropy->get_data()));
MCHK(mbedtls_x509write_crt_set_subject_name(crt.get(), subject));
MCHK(mbedtls_x509write_crt_set_serial(crt.get(), serial.get()));
// Note: 825-day validity range
// https://support.apple.com/en-us/HT210176
MCHK(mbedtls_x509write_crt_set_validity(
crt.get(), "20191101000000", "20211231235959"));
MCHK(
mbedtls_x509write_crt_set_basic_constraints(crt.get(), ca ? 1 : 0, 0));
MCHK(mbedtls_x509write_crt_set_subject_key_identifier(crt.get()));
MCHK(mbedtls_x509write_crt_set_authority_key_identifier(crt.get()));
// Because mbedtls does not support parsing x509v3 extensions from a
// CSR (https://github.com/ARMmbed/mbedtls/issues/2912), the CA sets the
// SAN directly instead of reading it from the CSR
try
{
MCHK(x509write_crt_set_subject_alt_names(crt.get(), subject_alt_names));
}
catch (const std::logic_error& err)
{
LOG_FAIL_FMT("Error writing SAN: {}", err.what());
return {};
}
uint8_t buf[4096];
memset(buf, 0, sizeof(buf));
MCHK(mbedtls_x509write_crt_pem(
crt.get(), buf, sizeof(buf), entropy->get_rng(), entropy->get_data()));
auto len = strlen((char*)buf);
return Pem(buf, len);
}
};
}

577
src/tls/key_pair_openssl.h Normal file
Просмотреть файл

@ -0,0 +1,577 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the Apache 2.0 License.
#pragma once
#include "key_pair_base.h"
#include <openssl/ec.h>
#include <openssl/engine.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/x509v3.h>
namespace tls
{
inline void OPENSSL_CHECK1(int rc)
{
unsigned long ec = ERR_get_error();
if (rc != 1 && ec != 0)
{
throw std::runtime_error(
fmt::format("OpenSSL error: {}", ERR_error_string(ec, NULL)));
}
}
inline void OPENSSL_CHECKNULL(void* ptr)
{
if (ptr == NULL)
{
throw std::runtime_error("OpenSSL error: missing object");
}
}
class PublicKey_OpenSSL : public PublicKeyBase
{
protected:
EVP_PKEY* key = nullptr;
PublicKey_OpenSSL() {}
inline const EVP_MD* get_md_type(MDType mdt) const
{
switch (mdt)
{
case MDType::NONE:
return nullptr;
case MDType::SHA1:
return EVP_sha1();
case MDType::SHA256:
return EVP_sha256();
case MDType::SHA384:
return EVP_sha384();
case MDType::SHA512:
return EVP_sha512();
default:
return nullptr;
}
return nullptr;
}
public:
PublicKey_OpenSSL(PublicKey_OpenSSL&& key) = default;
/**
* Construct from PEM
*/
PublicKey_OpenSSL(const Pem& pem)
{
BIO* mem = BIO_new_mem_buf(pem.data(), -1);
key = PEM_read_bio_PUBKEY(mem, NULL, NULL, NULL);
BIO_free(mem);
if (!key)
throw std::runtime_error("could not parse PEM");
}
/**
* Construct from DER
*/
PublicKey_OpenSSL(const std::vector<uint8_t>& der)
{
const unsigned char* pp = der.data();
key = d2i_PublicKey(EVP_PKEY_EC, &key, &pp, der.size());
if (!key)
{
throw new std::runtime_error("Could not read DER");
}
}
/**
* Construct from a pre-initialised pk context
*/
PublicKey_OpenSSL(EVP_PKEY* key) : key(key) {}
virtual ~PublicKey_OpenSSL()
{
if (key)
EVP_PKEY_free(key);
}
virtual CurveID get_curve_id() const override
{
int nid =
EC_GROUP_get_curve_name(EC_KEY_get0_group(EVP_PKEY_get0_EC_KEY(key)));
switch (nid)
{
case NID_secp384r1:
return CurveID::SECP384R1;
case NID_X9_62_prime256v1:
return CurveID::SECP256R1;
default:
throw std::runtime_error(
fmt::format("Unknown OpenSSL curve {}", nid));
}
return CurveID::NONE;
}
using PublicKeyBase::verify;
using PublicKeyBase::verify_hash;
virtual bool verify(
const uint8_t* contents,
size_t contents_size,
const uint8_t* sig,
size_t sig_size,
MDType md_type,
HashBytes& bytes) override
{
if (md_type == MDType::NONE)
{
md_type = get_md_for_ec(get_curve_id());
}
OpenSSLHashProvider hp;
bytes = hp.Hash(contents, contents_size, md_type);
return verify_hash(bytes.data(), bytes.size(), sig, sig_size, md_type);
}
virtual bool verify_hash(
const uint8_t* hash,
size_t hash_size,
const uint8_t* sig,
size_t sig_size,
MDType md_type) override
{
if (md_type == MDType::NONE)
{
md_type = get_md_for_ec(get_curve_id());
}
EVP_PKEY_CTX* pctx = NULL;
OPENSSL_CHECKNULL(pctx = EVP_PKEY_CTX_new(key, NULL));
OPENSSL_CHECK1(EVP_PKEY_verify_init(pctx));
if (md_type != MDType::NONE)
{
OPENSSL_CHECK1(
EVP_PKEY_CTX_set_signature_md(pctx, get_md_type(md_type)));
}
int rc = EVP_PKEY_verify(pctx, sig, sig_size, hash, hash_size);
EVP_PKEY_CTX_free(pctx);
bool ok = rc == 1;
if (!ok)
{
int ec = ERR_get_error();
LOG_DEBUG_FMT(
"OpenSSL signature verification failure: {}",
ERR_error_string(ec, NULL));
}
return ok;
}
/**
* Get the public key in PEM format
*/
virtual Pem public_key_pem() const override
{
BIO* buf = BIO_new(BIO_s_mem());
if (!buf)
throw std::runtime_error("out of memory");
OPENSSL_CHECK1(PEM_write_bio_PUBKEY(buf, key));
BUF_MEM* bptr;
BIO_get_mem_ptr(buf, &bptr);
Pem result((uint8_t*)bptr->data, bptr->length);
BIO_free(buf);
return result;
}
// EVP_PKEY* get_raw_context() const
// {
// return key;
// }
};
class KeyPair_OpenSSL : public PublicKey_OpenSSL, public KeyPairBase
{
protected:
inline int get_openssl_group_id(CurveID gid)
{
switch (gid)
{
case CurveID::NONE:
return NID_undef;
case CurveID::SECP384R1:
return NID_secp384r1;
case CurveID::SECP256R1:
return NID_X9_62_prime256v1;
default:
throw std::logic_error(
fmt::format("unsupported OpenSSL CurveID {}", gid));
}
return MBEDTLS_ECP_DP_NONE;
}
static std::vector<std::pair<std::string, std::string>> parse_name(
const std::string& name)
{
std::vector<std::pair<std::string, std::string>> r;
char* name_cpy = strdup(name.c_str());
char* p = std::strtok(name_cpy, ",");
while (p)
{
char* eq = strchr(p, '=');
*eq = '\0';
r.push_back(std::make_pair(p, eq + 1));
p = std::strtok(NULL, ",");
}
free(name_cpy);
return r;
}
public:
/**
* Generate a fresh key
*/
KeyPair_OpenSSL(CurveID curve_id)
{
int curve_nid = get_openssl_group_id(curve_id);
key = EVP_PKEY_new();
EVP_PKEY_CTX* pkctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL);
if (
EVP_PKEY_paramgen_init(pkctx) < 0 ||
EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pkctx, curve_nid) < 0 ||
EVP_PKEY_CTX_set_ec_param_enc(pkctx, OPENSSL_EC_NAMED_CURVE) < 0)
throw std::runtime_error("could not initialize PK context");
if (EVP_PKEY_keygen_init(pkctx) < 0 || EVP_PKEY_keygen(pkctx, &key) < 0)
throw std::runtime_error("could not generate new EC key");
EVP_PKEY_CTX_free(pkctx);
}
KeyPair_OpenSSL(const KeyPair_OpenSSL&) = delete;
KeyPair_OpenSSL(const Pem& pem, CBuffer pw = nullb)
{
BIO* mem = BIO_new_mem_buf(pem.data(), -1);
key = PEM_read_bio_PrivateKey(mem, NULL, NULL, (void*)pw.p);
BIO_free(mem);
if (!key)
throw std::runtime_error("could not parse PEM");
}
virtual ~KeyPair_OpenSSL() = default;
using PublicKey_OpenSSL::verify;
virtual bool verify(
const std::vector<uint8_t>& contents,
const std::vector<uint8_t>& signature) override
{
return PublicKey_OpenSSL::verify(contents, signature);
}
/**
* Get the private key in PEM format
*/
virtual Pem private_key_pem() const override
{
BIO* buf = BIO_new(BIO_s_mem());
if (!buf)
throw std::runtime_error("out of memory");
OPENSSL_CHECK1(
PEM_write_bio_PrivateKey(buf, key, NULL, NULL, 0, NULL, NULL));
BUF_MEM* bptr;
BIO_get_mem_ptr(buf, &bptr);
Pem result((uint8_t*)bptr->data, bptr->length);
BIO_free(buf);
return result;
}
/**
* Get the public key in PEM format
*/
virtual Pem public_key_pem() const override
{
return PublicKey_OpenSSL::public_key_pem();
}
/**
* Create signature over hash of data from private key.
*
* @param d data
*
* @return Signature as a vector
*/
virtual std::vector<uint8_t> sign(
CBuffer d, MDType md_type = {}) const override
{
if (md_type == MDType::NONE)
{
md_type = get_md_for_ec(get_curve_id());
}
OpenSSLHashProvider hp;
HashBytes hash = hp.Hash(d.p, d.rawSize(), md_type);
return sign_hash(hash.data(), hash.size());
}
/**
* Write signature over hash of data, and the size of that signature to
* specified locations.
*
* @param d data
* @param sig_size location to which the signature size will be written.
* Initial value should be max size of sig
* @param sig location to which the signature will be written
*
* @return 0 if successful, otherwise OpenSSL error code
*/
int sign(
CBuffer d, size_t* sig_size, uint8_t* sig, MDType md_type = {}) const
{
if (md_type == MDType::NONE)
{
md_type = get_md_for_ec(get_curve_id());
}
OpenSSLHashProvider hp;
HashBytes hash = hp.Hash(d.p, d.rawSize(), md_type);
return sign_hash(hash.data(), hash.size(), sig_size, sig);
}
/**
* Create signature over hashed data.
*
* @param hash First byte in hash sequence
* @param hash_size Number of bytes in hash sequence
*
* @return Signature as a vector
*/
std::vector<uint8_t> sign_hash(
const uint8_t* hash, size_t hash_size) const override
{
std::vector<uint8_t> sig(EVP_PKEY_size(key));
size_t written = sig.size();
if (sign_hash(hash, hash_size, &written, sig.data()) != 0)
{
return {};
}
sig.resize(written);
return sig;
}
virtual int sign_hash(
const uint8_t* hash,
size_t hash_size,
size_t* sig_size,
uint8_t* sig) const override
{
EVP_PKEY_CTX* pctx = NULL;
OPENSSL_CHECKNULL(pctx = EVP_PKEY_CTX_new(key, NULL));
OPENSSL_CHECK1(EVP_PKEY_sign_init(pctx));
OPENSSL_CHECK1(EVP_PKEY_sign(pctx, sig, sig_size, hash, hash_size));
EVP_PKEY_CTX_free(pctx);
return 0;
}
/**
* Create a certificate signing request for this key pair. If we were
* loaded from a private key, there will be no public key available for
* this call.
*/
virtual Pem create_csr(const std::string& name) const override
{
X509_REQ* req = NULL;
if (!(req = X509_REQ_new()))
{
throw std::runtime_error("failed to create X509_REQ object");
}
OPENSSL_CHECK1(X509_REQ_set_pubkey(req, key));
X509_NAME* subj_name = NULL;
OPENSSL_CHECKNULL(subj_name = X509_NAME_new());
for (auto kv : parse_name(name))
{
OPENSSL_CHECK1(X509_NAME_add_entry_by_txt(
subj_name,
kv.first.c_str(),
MBSTRING_ASC,
(const unsigned char*)kv.second.c_str(),
-1,
-1,
0));
}
OPENSSL_CHECK1(X509_REQ_set_subject_name(req, subj_name));
X509_NAME_free(subj_name);
if (key)
OPENSSL_CHECK1(X509_REQ_sign(req, key, EVP_sha512()));
BIO* mem = BIO_new(BIO_s_mem());
OPENSSL_CHECK1(PEM_write_bio_X509_REQ(mem, req));
BUF_MEM* bptr;
BIO_get_mem_ptr(mem, &bptr);
Pem result((uint8_t*)bptr->data, bptr->length);
BIO_free(mem);
X509_REQ_free(req);
return result;
}
virtual Pem sign_csr(
const Pem& issuer_cert,
const Pem& signing_request,
const std::vector<SubjectAltName> subject_alt_names,
bool ca = false) const override
{
X509* icrt = NULL;
X509_REQ* csr = NULL;
BIO* mem = BIO_new_mem_buf(signing_request.data(), -1);
OPENSSL_CHECKNULL(csr = PEM_read_bio_X509_REQ(mem, NULL, NULL, NULL));
BIO_free(mem);
X509* crt = NULL;
OPENSSL_CHECKNULL(crt = X509_new());
OPENSSL_CHECK1(X509_set_version(crt, 2));
// Add serial number
unsigned char rndbytes[16];
OPENSSL_CHECK1(RAND_bytes(rndbytes, sizeof(rndbytes)));
BIGNUM* bn = NULL;
OPENSSL_CHECKNULL(bn = BN_new());
BN_bin2bn(rndbytes, sizeof(rndbytes), bn);
ASN1_INTEGER* serial = ASN1_INTEGER_new();
BN_to_ASN1_INTEGER(bn, serial);
OPENSSL_CHECK1(X509_set_serialNumber(crt, serial));
ASN1_INTEGER_free(serial);
BN_free(bn);
// Add issuer name
if (!issuer_cert.empty())
{
mem = BIO_new_mem_buf(issuer_cert.data(), -1);
OPENSSL_CHECKNULL(icrt = PEM_read_bio_X509(mem, NULL, NULL, NULL));
BIO_free(mem);
OPENSSL_CHECK1(X509_set_issuer_name(crt, X509_get_subject_name(icrt)));
}
else
{
OPENSSL_CHECK1(
X509_set_issuer_name(crt, X509_REQ_get_subject_name(csr)));
}
// Note: 825-day validity range
// https://support.apple.com/en-us/HT210176
ASN1_TIME *before = NULL, *after = NULL;
OPENSSL_CHECKNULL(before = ASN1_TIME_new());
OPENSSL_CHECKNULL(after = ASN1_TIME_new());
OPENSSL_CHECK1(ASN1_TIME_set_string(before, "20191101000000Z"));
OPENSSL_CHECK1(ASN1_TIME_set_string(after, "20211231235959Z"));
X509_set1_notBefore(crt, before);
X509_set1_notAfter(crt, after);
ASN1_TIME_free(before);
ASN1_TIME_free(after);
X509_set_subject_name(crt, X509_REQ_get_subject_name(csr));
EVP_PKEY* req_pubkey = X509_REQ_get_pubkey(csr);
X509_set_pubkey(crt, req_pubkey);
EVP_PKEY_free(req_pubkey);
// Extensions
X509V3_CTX v3ctx;
X509V3_set_ctx_nodb(&v3ctx);
X509V3_set_ctx(&v3ctx, icrt ? icrt : crt, NULL, csr, NULL, 0);
// Add basic constraints
X509_EXTENSION* ext = NULL;
OPENSSL_CHECKNULL(
ext = X509V3_EXT_conf_nid(
NULL, &v3ctx, NID_basic_constraints, ca ? "CA:TRUE" : "CA:FALSE"));
OPENSSL_CHECK1(X509_add_ext(crt, ext, -1));
X509_EXTENSION_free(ext);
// Add subject key identifier
OPENSSL_CHECKNULL(
ext = X509V3_EXT_conf_nid(
NULL, &v3ctx, NID_subject_key_identifier, "hash"));
OPENSSL_CHECK1(X509_add_ext(crt, ext, -1));
X509_EXTENSION_free(ext);
// Add authority key identifier
OPENSSL_CHECKNULL(
ext = X509V3_EXT_conf_nid(
NULL, &v3ctx, NID_authority_key_identifier, "keyid:always"));
OPENSSL_CHECK1(X509_add_ext(crt, ext, -1));
X509_EXTENSION_free(ext);
// Subject alternative names (Necessary? Shouldn't they be in the CSR?)
if (!subject_alt_names.empty())
{
std::string all_alt_names;
bool first = true;
for (auto san : subject_alt_names)
{
if (first)
{
first = !first;
}
else
{
all_alt_names += ", ";
}
if (san.is_ip)
all_alt_names += "IP:";
else
all_alt_names += "DNS:";
all_alt_names += san.san;
}
OPENSSL_CHECKNULL(
ext = X509V3_EXT_conf_nid(
NULL, &v3ctx, NID_subject_alt_name, all_alt_names.c_str()));
OPENSSL_CHECK1(X509_add_ext(crt, ext, -1));
X509_EXTENSION_free(ext);
}
// Sign
auto md = get_md_type(get_md_for_ec(get_curve_id()));
int size = X509_sign(crt, key, md);
if (size <= 0)
throw std::runtime_error("could not sign CRT");
mem = BIO_new(BIO_s_mem());
OPENSSL_CHECK1(PEM_write_bio_X509(mem, crt));
// Export
BUF_MEM* bptr;
BIO_get_mem_ptr(mem, &bptr);
Pem result((uint8_t*)bptr->data, bptr->length);
BIO_free(mem);
X509_REQ_free(csr);
X509_free(crt);
if (icrt)
X509_free(icrt);
return result;
}
};
}

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

@ -4,6 +4,11 @@
#include "key_pair.h"
#include <algorithm>
#include <openssl/bn.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/rsa.h>
#include <optional>
#include <vector>
@ -14,12 +19,37 @@ namespace tls
static constexpr auto rsa_padding_mode = MBEDTLS_RSA_PKCS_V21;
static constexpr auto rsa_padding_digest_id = MBEDTLS_MD_SHA256;
class RSAPublicKey : public PublicKey
class RSAPublicKey_mbedTLS : public PublicKey_mbedTLS
{
public:
RSAPublicKey() = default;
RSAPublicKey_mbedTLS() = default;
RSAPublicKey(mbedtls::PKContext&& c) : PublicKey(std::move(c)) {}
RSAPublicKey_mbedTLS(mbedtls::PKContext&& c) :
PublicKey_mbedTLS(std::move(c))
{}
/**
* Construct from PEM
*/
RSAPublicKey_mbedTLS(const Pem& pem) : PublicKey_mbedTLS(pem)
{
if (!mbedtls_pk_can_do(ctx.get(), MBEDTLS_PK_RSA))
{
throw std::logic_error("invalid RSA key");
}
}
/**
* Construct from DER
*/
RSAPublicKey_mbedTLS(const std::vector<uint8_t>& der) :
PublicKey_mbedTLS(der)
{
if (!mbedtls_pk_can_do(ctx.get(), MBEDTLS_PK_RSA))
{
throw std::logic_error("invalid RSA key");
}
}
/**
* Wrap data using RSA-OAEP-256
@ -91,7 +121,126 @@ namespace tls
}
};
class RSAKeyPair : public RSAPublicKey
class RSAPublicKey_OpenSSL : public PublicKey_OpenSSL
{
public:
RSAPublicKey_OpenSSL() = default;
RSAPublicKey_OpenSSL(EVP_PKEY* c) : PublicKey_OpenSSL(c)
{
if (!EVP_PKEY_get0_RSA(key))
{
throw std::logic_error("invalid RSA key");
}
}
/**
* Construct from PEM
*/
RSAPublicKey_OpenSSL(const Pem& pem)
{
BIO* mem = BIO_new_mem_buf(pem.data(), -1);
key = PEM_read_bio_PUBKEY(mem, NULL, NULL, NULL);
BIO_free(mem);
if (!key || !EVP_PKEY_get0_RSA(key))
{
throw std::logic_error("invalid RSA key");
}
}
/**
* Construct from DER
*/
RSAPublicKey_OpenSSL(const std::vector<uint8_t>& der)
{
const unsigned char* pp = der.data();
RSA* rsa = NULL;
if (
((rsa = d2i_RSA_PUBKEY(NULL, &pp, der.size())) ==
NULL) && // "SubjectPublicKeyInfo structure" format
((rsa = d2i_RSAPublicKey(NULL, &pp, der.size())) ==
NULL)) // PKCS#1 structure format
{
unsigned long ec = ERR_get_error();
const char* msg = ERR_error_string(ec, NULL);
throw new std::runtime_error(fmt::format("OpenSSL error: {}", msg));
}
key = EVP_PKEY_new();
OPENSSL_CHECK1(EVP_PKEY_set1_RSA(key, rsa));
RSA_free(rsa);
}
/**
* Wrap data using RSA-OAEP-256
*
* @param input Pointer to raw data to wrap
* @param input_size Size of raw data
* @param label Optional string used as label during wrapping
* @param label Optional string used as label during wrapping
*
* @return Wrapped data
*/
std::vector<uint8_t> wrap(
const uint8_t* input,
size_t input_size,
const uint8_t* label = nullptr,
size_t label_size = 0)
{
EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new(key, NULL);
OPENSSL_CHECK1(EVP_PKEY_encrypt_init(ctx));
EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING);
EVP_PKEY_CTX_set_rsa_oaep_md(ctx, EVP_sha256());
EVP_PKEY_CTX_set_rsa_mgf1_md(ctx, EVP_sha256());
if (label)
{
unsigned char* openssl_label =
(unsigned char*)OPENSSL_malloc(label_size);
std::copy(label, label + label_size, openssl_label);
EVP_PKEY_CTX_set0_rsa_oaep_label(ctx, openssl_label, label_size);
}
else
{
EVP_PKEY_CTX_set0_rsa_oaep_label(ctx, NULL, 0);
}
size_t olen;
OPENSSL_CHECK1(EVP_PKEY_encrypt(ctx, NULL, &olen, input, input_size));
std::vector<uint8_t> output(olen);
OPENSSL_CHECK1(
EVP_PKEY_encrypt(ctx, output.data(), &olen, input, input_size));
output.resize(olen);
return output;
}
/**
* Wrap data using RSA-OAEP-256
*
* @param input Raw data to wrap
* @param label Optional string used as label during wrapping
*
* @return Wrapped data
*/
std::vector<uint8_t> wrap(
const std::vector<uint8_t>& input,
std::optional<std::string> label = std::nullopt)
{
const unsigned char* label_ = NULL;
size_t label_size = 0;
if (label.has_value())
{
label_ = reinterpret_cast<const unsigned char*>(label->c_str());
label_size = label->size();
}
return wrap(input.data(), input.size(), label_, label_size);
}
};
class RSAKeyPair_mbedTLS : public RSAPublicKey_mbedTLS
{
public:
static constexpr size_t default_public_key_size = 2048;
@ -100,7 +249,7 @@ namespace tls
/**
* Create a new public / private RSA key pair
*/
RSAKeyPair(
RSAKeyPair_mbedTLS(
size_t public_key_size = default_public_key_size,
size_t public_exponent = default_public_exponent)
{
@ -127,9 +276,24 @@ namespace tls
}
}
RSAKeyPair(mbedtls::PKContext&& k) : RSAPublicKey(std::move(k)) {}
RSAKeyPair_mbedTLS(mbedtls::PKContext&& k) :
RSAPublicKey_mbedTLS(std::move(k))
{}
RSAKeyPair(const RSAKeyPair&) = delete;
RSAKeyPair_mbedTLS(const RSAKeyPair_mbedTLS&) = delete;
RSAKeyPair_mbedTLS(const Pem& pem, CBuffer pw = nullb) :
RSAPublicKey_mbedTLS()
{
// keylen is +1 to include terminating null byte
int rc =
mbedtls_pk_parse_key(ctx.get(), pem.data(), pem.size(), pw.p, pw.n);
if (rc != 0)
{
throw std::logic_error(
"Could not parse private key: " + error_string(rc));
}
}
/**
* Unwrap data using RSA-OAEP-256
@ -180,6 +344,97 @@ namespace tls
}
};
class RSAKeyPair_OpenSSL : public RSAPublicKey_OpenSSL
{
public:
static constexpr size_t default_public_key_size = 2048;
static constexpr size_t default_public_exponent = 65537;
/**
* Create a new public / private RSA key pair
*/
RSAKeyPair_OpenSSL(
size_t public_key_size = default_public_key_size,
size_t public_exponent = default_public_exponent)
{
RSA* rsa = NULL;
BIGNUM* big_exp = NULL;
OPENSSL_CHECKNULL(big_exp = BN_new());
OPENSSL_CHECK1(BN_set_word(big_exp, public_exponent));
OPENSSL_CHECKNULL(rsa = RSA_new());
OPENSSL_CHECK1(RSA_generate_key_ex(rsa, public_key_size, big_exp, NULL));
OPENSSL_CHECKNULL(key = EVP_PKEY_new());
OPENSSL_CHECK1(EVP_PKEY_set1_RSA(key, rsa));
BN_free(big_exp);
RSA_free(rsa);
}
RSAKeyPair_OpenSSL(EVP_PKEY* k) : RSAPublicKey_OpenSSL(std::move(k)) {}
RSAKeyPair_OpenSSL(const RSAKeyPair_OpenSSL&) = delete;
RSAKeyPair_OpenSSL(const Pem& pem, CBuffer pw = nullb)
{
BIO* mem = BIO_new_mem_buf(pem.data(), -1);
key = PEM_read_bio_PrivateKey(mem, NULL, NULL, (void*)pw.p);
BIO_free(mem);
if (!key)
{
throw std::runtime_error("could not parse PEM");
}
}
/**
* Unwrap data using RSA-OAEP-256
*
* @param input Raw data to unwrap
* @param label Optional string used as label during unwrapping
*
* @return Unwrapped data
*/
std::vector<uint8_t> unwrap(
const std::vector<uint8_t>& input,
std::optional<std::string> label = std::nullopt)
{
const unsigned char* label_ = NULL;
size_t label_size = 0;
if (label.has_value())
{
label_ = reinterpret_cast<const unsigned char*>(label->c_str());
label_size = label->size();
}
EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new(key, NULL);
OPENSSL_CHECK1(EVP_PKEY_decrypt_init(ctx));
EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING);
EVP_PKEY_CTX_set_rsa_oaep_md(ctx, EVP_sha256());
EVP_PKEY_CTX_set_rsa_mgf1_md(ctx, EVP_sha256());
if (label_)
{
unsigned char* openssl_label =
(unsigned char*)OPENSSL_malloc(label_size);
std::copy(label_, label_ + label_size, openssl_label);
EVP_PKEY_CTX_set0_rsa_oaep_label(ctx, openssl_label, label_size);
}
else
EVP_PKEY_CTX_set0_rsa_oaep_label(ctx, NULL, 0);
size_t olen;
OPENSSL_CHECK1(
EVP_PKEY_decrypt(ctx, NULL, &olen, input.data(), input.size()));
std::vector<uint8_t> output(olen);
OPENSSL_CHECK1(EVP_PKEY_decrypt(
ctx, output.data(), &olen, input.data(), input.size()));
output.resize(olen);
return output;
}
};
using RSAPublicKey = RSAPublicKey_OpenSSL;
using RSAKeyPair = RSAKeyPair_OpenSSL;
using RSAKeyPairPtr = std::shared_ptr<RSAKeyPair>;
using RSAPublicKeyPtr = std::shared_ptr<RSAPublicKey>;
@ -198,30 +453,26 @@ namespace tls
*/
inline RSAKeyPairPtr make_rsa_key_pair(const Pem& pkey, CBuffer pw = nullb)
{
auto key = parse_private_key(pkey, pw);
return std::make_shared<RSAKeyPair>(std::move(key));
return std::make_shared<RSAKeyPair>(pkey, pw);
}
inline RSAPublicKeyPtr make_rsa_public_key(
const uint8_t* public_pem_data, size_t public_pem_size)
inline RSAPublicKeyPtr make_rsa_public_key(const std::vector<uint8_t>& der)
{
auto ctx = mbedtls::make_unique<mbedtls::PKContext>();
return std::make_shared<RSAPublicKey>(der);
}
int rc =
mbedtls_pk_parse_public_key(ctx.get(), public_pem_data, public_pem_size);
if (rc != 0)
inline RSAPublicKeyPtr make_rsa_public_key(const uint8_t* data, size_t size)
{
if (size < 10 || strncmp("-----BEGIN", (char*)data, 10) != 0)
{
throw std::logic_error(
fmt::format("Could not parse public key PEM: {}", error_string(rc)));
std::vector<uint8_t> der = {data, data + size};
return std::make_shared<RSAPublicKey>(der);
}
if (ctx->pk_info != mbedtls_pk_info_from_type(MBEDTLS_PK_RSA))
else
{
throw std::logic_error(
"Could not make RSA public key as PEM does not appear to be valid RSA");
Pem pem(data, size);
return std::make_shared<RSAPublicKey>(pem);
}
return std::make_shared<RSAPublicKey>(std::move(ctx));
}
inline RSAPublicKeyPtr make_rsa_public_key(const Pem& public_pem)

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

@ -1,11 +1,12 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the Apache 2.0 License.
#define PICOBENCH_IMPLEMENT_WITH_MAIN
#include "../key_pair.h"
#define PICOBENCH_IMPLEMENT_WITH_MAIN
#include <picobench/picobench.hpp>
using namespace std;
using namespace tls;
static const string lorem_ipsum =
"Lorem ipsum dolor sit amet, consectetur adipiscing "
@ -43,154 +44,246 @@ vector<uint8_t> make_contents()
return contents;
}
template <tls::CurveImpl Curve, size_t NContents>
template <typename P, CurveID Curve, size_t NContents>
static void benchmark_sign(picobench::state& s)
{
auto kp = tls::make_key_pair(Curve);
const auto contents = make_contents<NContents>();
P kp(Curve);
auto contents = make_contents<NContents>();
s.start_timer();
for (auto _ : s)
{
(void)_;
auto signature = kp->sign(contents);
auto signature = kp.sign(contents);
do_not_optimize(signature);
clobber_memory();
}
s.stop_timer();
}
template <tls::CurveImpl Curve, size_t NContents>
template <typename T, typename S, CurveID CID, size_t NContents>
static void benchmark_verify(picobench::state& s)
{
auto kp = tls::make_key_pair(Curve);
T kp(CID);
const auto contents = make_contents<NContents>();
S pubk(kp.public_key_pem());
auto signature = kp->sign(contents);
const auto public_key = kp->public_key_pem();
auto pubk = tls::make_public_key(
public_key, Curve == tls::CurveImpl::secp256k1_bitcoin);
auto signature = kp.sign(contents);
s.start_timer();
for (auto _ : s)
{
(void)_;
auto verified = kp->verify(contents, signature);
auto verified = pubk.verify(contents, signature);
do_not_optimize(verified);
clobber_memory();
}
s.stop_timer();
}
template <tls::CurveImpl Curve, size_t NContents>
template <typename P, MDType M, size_t NContents>
static void benchmark_hash(picobench::state& s)
{
auto kp = tls::make_key_pair(Curve);
const auto contents = make_contents<NContents>();
s.start_timer();
for (auto _ : s)
{
(void)_;
std::vector<uint8_t> hash;
tls::do_hash(
*kp->get_raw_context(), contents.data(), contents.size(), hash);
P hp;
HashBytes hash = hp.Hash(contents.data(), contents.size(), M);
do_not_optimize(hash);
clobber_memory();
}
s.stop_timer();
}
const std::vector<int> sizes = {1};
const std::vector<int> sizes = {10};
using namespace tls;
#define PICO_SUFFIX(CURVE) \
iterations(sizes).samples(10).baseline( \
CURVE == CurveImpl::service_identity_curve_choice)
#define PICO_SUFFIX(CURVE) iterations(sizes).samples(10)
PICOBENCH_SUITE("sign");
namespace
#define PICO_HASH_SUFFIX() iterations(sizes).samples(10)
PICOBENCH_SUITE("sign secp384r1");
namespace SIGN_SECP384R1
{
auto sign_384_1byte = benchmark_sign<CurveImpl::secp384r1, 1>;
PICOBENCH(sign_384_1byte).PICO_SUFFIX(CurveImpl::secp384r1);
auto sign_256k1_mbed_1byte = benchmark_sign<CurveImpl::secp256k1_mbedtls, 1>;
PICOBENCH(sign_256k1_mbed_1byte).PICO_SUFFIX(CurveImpl::secp256k1_mbedtls);
auto sign_256k1_bitc_1byte = benchmark_sign<CurveImpl::secp256k1_bitcoin, 1>;
PICOBENCH(sign_256k1_bitc_1byte).PICO_SUFFIX(CurveImpl::secp256k1_bitcoin);
auto sign_384_mbed_1byte =
benchmark_sign<KeyPair_mbedTLS, CurveID::SECP384R1, 1>;
PICOBENCH(sign_384_mbed_1byte).PICO_SUFFIX(CurveID::SECP384R1);
auto sign_384_ossl_1byte =
benchmark_sign<KeyPair_OpenSSL, CurveID::SECP384R1, 1>;
PICOBENCH(sign_384_ossl_1byte).PICO_SUFFIX(CurveID::SECP384R1);
auto sign_384_1k = benchmark_sign<CurveImpl::secp384r1, 1024>;
PICOBENCH(sign_384_1k).PICO_SUFFIX(CurveImpl::secp384r1);
auto sign_256k1_mbed_1k = benchmark_sign<CurveImpl::secp256k1_mbedtls, 1024>;
PICOBENCH(sign_256k1_mbed_1k).PICO_SUFFIX(CurveImpl::secp256k1_mbedtls);
auto sign_256k1_bitc_1k = benchmark_sign<CurveImpl::secp256k1_bitcoin, 1024>;
PICOBENCH(sign_256k1_bitc_1k).PICO_SUFFIX(CurveImpl::secp256k1_bitcoin);
auto sign_384_mbed_1k =
benchmark_sign<KeyPair_mbedTLS, CurveID::SECP384R1, 1024>;
PICOBENCH(sign_384_mbed_1k).PICO_SUFFIX(CurveID::SECP384R1);
auto sign_384_ossl_1k =
benchmark_sign<KeyPair_OpenSSL, CurveID::SECP384R1, 1024>;
PICOBENCH(sign_384_ossl_1k).PICO_SUFFIX(CurveID::SECP384R1);
auto sign_384_100k = benchmark_sign<CurveImpl::secp384r1, 102400>;
PICOBENCH(sign_384_100k).PICO_SUFFIX(CurveImpl::secp384r1);
auto sign_256k1_mbed_100k =
benchmark_sign<CurveImpl::secp256k1_mbedtls, 102400>;
PICOBENCH(sign_256k1_mbed_100k).PICO_SUFFIX(CurveImpl::secp256k1_mbedtls);
auto sign_256k1_bitc_100k =
benchmark_sign<CurveImpl::secp256k1_bitcoin, 102400>;
PICOBENCH(sign_256k1_bitc_100k).PICO_SUFFIX(CurveImpl::secp256k1_bitcoin);
auto sign_384_mbed_100k =
benchmark_sign<KeyPair_mbedTLS, CurveID::SECP384R1, 102400>;
PICOBENCH(sign_384_mbed_100k).PICO_SUFFIX(CurveID::SECP384R1);
auto sign_384_ossl_100k =
benchmark_sign<KeyPair_OpenSSL, CurveID::SECP384R1, 102400>;
PICOBENCH(sign_384_ossl_100k).PICO_SUFFIX(CurveID::SECP384R1);
}
PICOBENCH_SUITE("verify");
namespace
PICOBENCH_SUITE("sign secp256r1");
namespace SIGN_SECP256R1
{
auto verify_384_1byte = benchmark_verify<CurveImpl::secp384r1, 1>;
PICOBENCH(verify_384_1byte).PICO_SUFFIX(CurveImpl::secp384r1);
auto verify_256k1_mbed_1byte =
benchmark_verify<CurveImpl::secp256k1_mbedtls, 1>;
PICOBENCH(verify_256k1_mbed_1byte).PICO_SUFFIX(CurveImpl::secp256k1_mbedtls);
auto verify_256k1_bitc_1byte =
benchmark_verify<CurveImpl::secp256k1_bitcoin, 1>;
PICOBENCH(verify_256k1_bitc_1byte).PICO_SUFFIX(CurveImpl::secp256k1_bitcoin);
auto sign_256r1_mbed_1byte =
benchmark_sign<KeyPair_mbedTLS, CurveID::SECP256R1, 1>;
PICOBENCH(sign_256r1_mbed_1byte).PICO_SUFFIX(CurveID::SECP256R1);
auto sign_256r1_ossl_1byte =
benchmark_sign<KeyPair_OpenSSL, CurveID::SECP256R1, 1>;
PICOBENCH(sign_256r1_ossl_1byte).PICO_SUFFIX(CurveID::SECP256R1);
auto verify_384_1k = benchmark_verify<CurveImpl::secp384r1, 1024>;
PICOBENCH(verify_384_1k).PICO_SUFFIX(CurveImpl::secp384r1);
auto verify_256k1_mbed_1k =
benchmark_verify<CurveImpl::secp256k1_mbedtls, 1024>;
PICOBENCH(verify_256k1_mbed_1k).PICO_SUFFIX(CurveImpl::secp256k1_mbedtls);
auto verify_256k1_bitc_1k =
benchmark_verify<CurveImpl::secp256k1_bitcoin, 1024>;
PICOBENCH(verify_256k1_bitc_1k).PICO_SUFFIX(CurveImpl::secp256k1_bitcoin);
auto sign_256r1_mbed_1k =
benchmark_sign<KeyPair_mbedTLS, CurveID::SECP256R1, 1024>;
PICOBENCH(sign_256r1_mbed_1k).PICO_SUFFIX(CurveID::SECP256R1);
auto sign_256r1_ossl_1k =
benchmark_sign<KeyPair_OpenSSL, CurveID::SECP256R1, 1024>;
PICOBENCH(sign_256r1_ossl_1k).PICO_SUFFIX(CurveID::SECP256R1);
auto verify_384_100k = benchmark_verify<CurveImpl::secp384r1, 102400>;
PICOBENCH(verify_384_100k).PICO_SUFFIX(CurveImpl::secp384r1);
auto verify_256k1_mbed_100k =
benchmark_verify<CurveImpl::secp256k1_mbedtls, 102400>;
PICOBENCH(verify_256k1_mbed_100k).PICO_SUFFIX(CurveImpl::secp256k1_mbedtls);
auto verify_256k1_bitc_100k =
benchmark_verify<CurveImpl::secp256k1_bitcoin, 102400>;
PICOBENCH(verify_256k1_bitc_100k).PICO_SUFFIX(CurveImpl::secp256k1_bitcoin);
auto sign_256r1_mbed_100k =
benchmark_sign<KeyPair_mbedTLS, CurveID::SECP256R1, 102400>;
PICOBENCH(sign_256r1_mbed_100k).PICO_SUFFIX(CurveID::SECP256R1);
auto sign_256r1_ossl_100k =
benchmark_sign<KeyPair_OpenSSL, CurveID::SECP256R1, 102400>;
PICOBENCH(sign_256r1_ossl_100k).PICO_SUFFIX(CurveID::SECP256R1);
}
PICOBENCH_SUITE("verify secp384r1");
namespace SECP384R1
{
auto verify_384_mbed_1byte =
benchmark_verify<KeyPair_mbedTLS, PublicKey_mbedTLS, CurveID::SECP384R1, 1>;
PICOBENCH(verify_384_mbed_1byte).PICO_SUFFIX(CurveID::SECP384R1);
auto verify_384_ossl_1byte =
benchmark_verify<KeyPair_OpenSSL, PublicKey_OpenSSL, CurveID::SECP384R1, 1>;
PICOBENCH(verify_384_ossl_1byte).PICO_SUFFIX(CurveID::SECP384R1);
auto verify_384_mbed_1k = benchmark_verify<
KeyPair_mbedTLS,
PublicKey_mbedTLS,
CurveID::SECP384R1,
1024>;
PICOBENCH(verify_384_mbed_1k).PICO_SUFFIX(CurveID::SECP384R1);
auto verify_384_ossl_1k = benchmark_verify<
KeyPair_OpenSSL,
PublicKey_OpenSSL,
CurveID::SECP384R1,
1024>;
PICOBENCH(verify_384_ossl_1k).PICO_SUFFIX(CurveID::SECP384R1);
auto verify_384_mbed_100k = benchmark_verify<
KeyPair_mbedTLS,
PublicKey_mbedTLS,
CurveID::SECP384R1,
102400>;
PICOBENCH(verify_384_mbed_100k).PICO_SUFFIX(CurveID::SECP384R1);
auto verify_384_ossl_100k = benchmark_verify<
KeyPair_OpenSSL,
PublicKey_OpenSSL,
CurveID::SECP384R1,
102400>;
PICOBENCH(verify_384_ossl_100k).PICO_SUFFIX(CurveID::SECP384R1);
}
PICOBENCH_SUITE("verify secp256r1");
namespace SECP256R1
{
auto verify_256r1_mbed_1byte =
benchmark_verify<KeyPair_mbedTLS, PublicKey_mbedTLS, CurveID::SECP256R1, 1>;
PICOBENCH(verify_256r1_mbed_1byte).PICO_SUFFIX(CurveID::SECP256R1);
auto verify_256r1_ossl_1byte =
benchmark_verify<KeyPair_OpenSSL, PublicKey_OpenSSL, CurveID::SECP256R1, 1>;
PICOBENCH(verify_256r1_ossl_1byte).PICO_SUFFIX(CurveID::SECP256R1);
auto verify_256r1_mbed_1k = benchmark_verify<
KeyPair_mbedTLS,
PublicKey_mbedTLS,
CurveID::SECP256R1,
1024>;
PICOBENCH(verify_256r1_mbed_1k).PICO_SUFFIX(CurveID::SECP256R1);
auto verify_256r1_ossl_1k = benchmark_verify<
KeyPair_OpenSSL,
PublicKey_OpenSSL,
CurveID::SECP256R1,
1024>;
PICOBENCH(verify_256r1_ossl_1k).PICO_SUFFIX(CurveID::SECP256R1);
auto verify_256r1_mbed_100k = benchmark_verify<
KeyPair_mbedTLS,
PublicKey_mbedTLS,
CurveID::SECP256R1,
102400>;
PICOBENCH(verify_256r1_mbed_100k).PICO_SUFFIX(CurveID::SECP256R1);
auto verify_256r1_ossl_100k = benchmark_verify<
KeyPair_OpenSSL,
PublicKey_OpenSSL,
CurveID::SECP256R1,
102400>;
PICOBENCH(verify_256r1_ossl_100k).PICO_SUFFIX(CurveID::SECP256R1);
}
PICOBENCH_SUITE("hash");
namespace
namespace Hashes
{
auto hash_384_1byte = benchmark_hash<CurveImpl::secp384r1, 1>;
PICOBENCH(hash_384_1byte).PICO_SUFFIX(CurveImpl::secp384r1);
auto hash_256k1_mbed_1byte = benchmark_hash<CurveImpl::secp256k1_mbedtls, 1>;
PICOBENCH(hash_256k1_mbed_1byte).PICO_SUFFIX(CurveImpl::secp256k1_mbedtls);
auto hash_256k1_bitc_1byte = benchmark_hash<CurveImpl::secp256k1_bitcoin, 1>;
PICOBENCH(hash_256k1_bitc_1byte).PICO_SUFFIX(CurveImpl::secp256k1_bitcoin);
auto sha_384_mbed_1byte = benchmark_hash<MBedHashProvider, MDType::SHA384, 1>;
PICOBENCH(sha_384_mbed_1byte).PICO_HASH_SUFFIX().baseline();
auto sha_256_mbed_1byte = benchmark_hash<MBedHashProvider, MDType::SHA256, 1>;
PICOBENCH(sha_256_mbed_1byte).PICO_HASH_SUFFIX();
auto sha_512_mbed_1byte = benchmark_hash<MBedHashProvider, MDType::SHA512, 1>;
PICOBENCH(sha_512_mbed_1byte).PICO_HASH_SUFFIX();
auto hash_384_1k = benchmark_hash<CurveImpl::secp384r1, 1024>;
PICOBENCH(hash_384_1k).PICO_SUFFIX(CurveImpl::secp384r1);
auto hash_256k1_mbed_1k = benchmark_hash<CurveImpl::secp256k1_mbedtls, 1024>;
PICOBENCH(hash_256k1_mbed_1k).PICO_SUFFIX(CurveImpl::secp256k1_mbedtls);
auto hash_256k1_bitc_1k = benchmark_hash<CurveImpl::secp256k1_bitcoin, 1024>;
PICOBENCH(hash_256k1_bitc_1k).PICO_SUFFIX(CurveImpl::secp256k1_bitcoin);
auto sha_384_ossl_1byte =
benchmark_hash<OpenSSLHashProvider, MDType::SHA384, 1>;
PICOBENCH(sha_384_ossl_1byte).PICO_HASH_SUFFIX();
auto sha_256_ossl_1byte =
benchmark_hash<OpenSSLHashProvider, MDType::SHA256, 1>;
PICOBENCH(sha_256_ossl_1byte).PICO_HASH_SUFFIX();
auto sha_512_ossl_1byte =
benchmark_hash<OpenSSLHashProvider, MDType::SHA512, 1>;
PICOBENCH(sha_512_ossl_1byte).PICO_HASH_SUFFIX();
auto hash_384_100k = benchmark_hash<CurveImpl::secp384r1, 102400>;
PICOBENCH(hash_384_100k).PICO_SUFFIX(CurveImpl::secp384r1);
auto hash_256k1_mbed_100k =
benchmark_hash<CurveImpl::secp256k1_mbedtls, 102400>;
PICOBENCH(hash_256k1_mbed_100k).PICO_SUFFIX(CurveImpl::secp256k1_mbedtls);
auto hash_256k1_bitc_100k =
benchmark_hash<CurveImpl::secp256k1_bitcoin, 102400>;
PICOBENCH(hash_256k1_bitc_100k).PICO_SUFFIX(CurveImpl::secp256k1_bitcoin);
auto sha_384_mbed_1k = benchmark_hash<MBedHashProvider, MDType::SHA384, 1024>;
PICOBENCH(sha_384_mbed_1k).PICO_HASH_SUFFIX();
auto sha_256_mbed_1k = benchmark_hash<MBedHashProvider, MDType::SHA256, 1024>;
PICOBENCH(sha_256_mbed_1k).PICO_HASH_SUFFIX();
auto sha_512_mbed_1k = benchmark_hash<MBedHashProvider, MDType::SHA512, 1024>;
PICOBENCH(sha_512_mbed_1k).PICO_HASH_SUFFIX();
auto sha_384_ossl_1k =
benchmark_hash<OpenSSLHashProvider, MDType::SHA384, 1024>;
PICOBENCH(sha_384_ossl_1k).PICO_HASH_SUFFIX();
auto sha_256_ossl_1k =
benchmark_hash<OpenSSLHashProvider, MDType::SHA256, 1024>;
PICOBENCH(sha_256_ossl_1k).PICO_HASH_SUFFIX();
auto sha_512_ossl_1k =
benchmark_hash<OpenSSLHashProvider, MDType::SHA512, 1024>;
PICOBENCH(sha_512_ossl_1k).PICO_HASH_SUFFIX();
auto sha_384_mbed_100k =
benchmark_hash<MBedHashProvider, MDType::SHA384, 102400>;
PICOBENCH(sha_384_mbed_100k).PICO_HASH_SUFFIX();
auto sha_256_mbed_100k =
benchmark_hash<MBedHashProvider, MDType::SHA256, 102400>;
PICOBENCH(sha_256_mbed_100k).PICO_HASH_SUFFIX();
auto sha_512_mbed_100k =
benchmark_hash<MBedHashProvider, MDType::SHA512, 102400>;
PICOBENCH(sha_512_mbed_100k).PICO_HASH_SUFFIX();
auto sha_384_ossl_100k =
benchmark_hash<OpenSSLHashProvider, MDType::SHA384, 102400>;
PICOBENCH(sha_384_ossl_100k).PICO_HASH_SUFFIX();
auto sha_256_ossl_100k =
benchmark_hash<OpenSSLHashProvider, MDType::SHA256, 102400>;
PICOBENCH(sha_256_ossl_100k).PICO_HASH_SUFFIX();
auto sha_512_ossl_100k =
benchmark_hash<OpenSSLHashProvider, MDType::SHA512, 102400>;
PICOBENCH(sha_512_ossl_100k).PICO_HASH_SUFFIX();
}

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

@ -24,8 +24,9 @@ int main(int argc, char** argv)
CLI11_PARSE(app, argc, argv);
auto kp = tls::make_key_pair();
auto cert = kp->sign_csr(
kp->create_csr(subject_name), subject_name, subject_alternative_names);
auto icrt = kp->self_sign("CN=issuer");
auto csr = kp->create_csr(subject_name);
auto cert = kp->sign_csr(icrt, csr, subject_alternative_names);
std::cout << cert.str() << std::endl;
return 0;

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

@ -56,8 +56,10 @@ TEST_CASE("Simple key exchange")
TEST_CASE("Key exchange from static shares")
{
auto peer1_kp = tls::make_key_pair();
auto peer2_kp = tls::make_key_pair();
auto peer1_kp =
std::make_shared<tls::KeyPair_mbedTLS>(tls::service_identity_curve_choice);
auto peer2_kp =
std::make_shared<tls::KeyPair_mbedTLS>(tls::service_identity_curve_choice);
auto peer1_ctx = tls::KeyExchangeContext(peer1_kp, peer2_kp);
auto peer2_ctx = tls::KeyExchangeContext(peer2_kp, peer1_kp);

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

@ -11,6 +11,7 @@
#include <string>
using namespace std;
using namespace tls;
static const string contents_ =
"Lorem ipsum dolor sit amet, consectetur adipiscing "
@ -31,25 +32,22 @@ void corrupt(T& buf)
buf[buf.size() - 2]++;
}
static constexpr tls::CurveImpl supported_curves[] = {
tls::CurveImpl::secp384r1,
tls::CurveImpl::secp256k1_mbedtls,
tls::CurveImpl::secp256k1_bitcoin};
static constexpr CurveID supported_curves[] = {CurveID::SECP384R1,
CurveID::SECP256R1};
static constexpr char const* labels[] = {
"secp384r1", "secp256k1_mbedtls", "secp256k1_bitcoin"};
static constexpr char const* labels[] = {"secp384r1", "secp256r1"};
TEST_CASE("Sign, verify, with KeyPair")
{
for (const auto curve : supported_curves)
{
INFO("With curve: " << labels[static_cast<size_t>(curve) - 1]);
auto kp = tls::make_key_pair(curve);
auto kp = make_key_pair(curve);
vector<uint8_t> contents(contents_.begin(), contents_.end());
const vector<uint8_t> signature = kp->sign(contents);
CHECK(kp->verify(contents, signature));
auto kp2 = tls::make_key_pair(kp->private_key_pem());
auto kp2 = make_key_pair(kp->private_key_pem());
CHECK(kp2->verify(contents, signature));
// Signatures won't necessarily be identical due to entropy, but should be
@ -68,12 +66,12 @@ TEST_CASE("Sign, verify, with PublicKey")
for (const auto curve : supported_curves)
{
INFO("With curve: " << labels[static_cast<size_t>(curve) - 1]);
auto kp = tls::make_key_pair(curve);
auto kp = make_key_pair(curve);
vector<uint8_t> contents(contents_.begin(), contents_.end());
const vector<uint8_t> signature = kp->sign(contents);
const auto public_key = kp->public_key_pem();
auto pubk = tls::make_public_key(public_key);
auto pubk = make_public_key(public_key);
CHECK(pubk->verify(contents, signature));
}
}
@ -83,12 +81,12 @@ TEST_CASE("Sign, fail to verify with bad signature")
for (const auto curve : supported_curves)
{
INFO("With curve: " << labels[static_cast<size_t>(curve) - 1]);
auto kp = tls::make_key_pair(curve);
auto kp = make_key_pair(curve);
vector<uint8_t> contents(contents_.begin(), contents_.end());
vector<uint8_t> signature = kp->sign(contents);
const auto public_key = kp->public_key_pem();
auto pubk = tls::make_public_key(public_key);
auto pubk = make_public_key(public_key);
corrupt(signature);
CHECK_FALSE(pubk->verify(contents, signature));
}
@ -99,12 +97,12 @@ TEST_CASE("Sign, fail to verify with bad contents")
for (const auto curve : supported_curves)
{
INFO("With curve: " << labels[static_cast<size_t>(curve) - 1]);
auto kp = tls::make_key_pair(curve);
auto kp = make_key_pair(curve);
vector<uint8_t> contents(contents_.begin(), contents_.end());
vector<uint8_t> signature = kp->sign(contents);
const auto public_key = kp->public_key_pem();
auto pubk = tls::make_public_key(public_key);
auto pubk = make_public_key(public_key);
corrupt(contents);
CHECK_FALSE(pubk->verify(contents, signature));
}
@ -115,13 +113,13 @@ TEST_CASE("Sign, fail to verify with wrong key on correct curve")
for (const auto curve : supported_curves)
{
INFO("With curve: " << labels[static_cast<size_t>(curve) - 1]);
auto kp = tls::make_key_pair(curve);
auto kp = make_key_pair(curve);
vector<uint8_t> contents(contents_.begin(), contents_.end());
vector<uint8_t> signature = kp->sign(contents);
auto kp2 = tls::make_key_pair(curve);
auto kp2 = make_key_pair(curve);
const auto public_key = kp2->public_key_pem();
auto pubk = tls::make_public_key(public_key);
auto pubk = make_public_key(public_key);
CHECK_FALSE(pubk->verify(contents, signature));
}
}
@ -131,42 +129,38 @@ TEST_CASE("Sign, fail to verify with wrong key on wrong curve")
for (const auto curve : supported_curves)
{
INFO("With curve: " << labels[static_cast<size_t>(curve) - 1]);
auto kp = tls::make_key_pair(curve);
auto kp = make_key_pair(curve);
vector<uint8_t> contents(contents_.begin(), contents_.end());
vector<uint8_t> signature = kp->sign(contents);
const auto wrong_curve = curve == tls::CurveImpl::secp384r1 ?
tls::CurveImpl::secp256k1_mbedtls :
tls::CurveImpl::secp384r1;
auto kp2 = tls::make_key_pair(wrong_curve);
const auto wrong_curve =
curve == CurveID::SECP384R1 ? CurveID::SECP256R1 : CurveID::SECP384R1;
auto kp2 = make_key_pair(wrong_curve);
const auto public_key = kp2->public_key_pem();
auto pubk = tls::make_public_key(public_key);
auto pubk = make_public_key(public_key);
CHECK_FALSE(pubk->verify(contents, signature));
}
}
using CurvePair = std::pair<tls::CurveImpl, tls::CurveImpl>;
std::vector<CurvePair> equivalent_curves{
std::make_pair(
tls::CurveImpl::secp256k1_mbedtls, tls::CurveImpl::secp256k1_bitcoin),
std::make_pair(
tls::CurveImpl::secp256k1_bitcoin, tls::CurveImpl::secp256k1_mbedtls)};
template <typename T, typename S, CurveID CID>
void run_alt()
{
T kp1(CID);
vector<uint8_t> contents(contents_.begin(), contents_.end());
vector<uint8_t> signature = kp1.sign(contents);
S kp2(kp1.public_key_pem());
CHECK(kp2.verify(contents, signature));
}
TEST_CASE("Sign, verify with alternate implementation")
{
for (const auto& curves : equivalent_curves)
{
INFO("Sign impl: " << labels[static_cast<size_t>(curves.first) - 1]);
INFO("Verify impl: " << labels[static_cast<size_t>(curves.second) - 1]);
auto kp = tls::make_key_pair(curves.first);
vector<uint8_t> contents(contents_.begin(), contents_.end());
vector<uint8_t> signature = kp->sign(contents);
const auto public_key = kp->public_key_pem();
auto pubk = tls::make_public_key(
public_key, curves.second == tls::CurveImpl::secp256k1_bitcoin);
CHECK(pubk->verify(contents, signature));
}
run_alt<KeyPair_mbedTLS, PublicKey_mbedTLS, CurveID::SECP256R1>();
run_alt<KeyPair_OpenSSL, PublicKey_OpenSSL, CurveID::SECP256R1>();
run_alt<KeyPair_OpenSSL, PublicKey_mbedTLS, CurveID::SECP384R1>();
run_alt<KeyPair_mbedTLS, PublicKey_OpenSSL, CurveID::SECP384R1>();
run_alt<KeyPair_OpenSSL, PublicKey_mbedTLS, CurveID::SECP256R1>();
run_alt<KeyPair_mbedTLS, PublicKey_OpenSSL, CurveID::SECP256R1>();
}
TEST_CASE("Sign, verify with certificate")
@ -174,12 +168,12 @@ TEST_CASE("Sign, verify with certificate")
for (const auto curve : supported_curves)
{
INFO("With curve: " << labels[static_cast<size_t>(curve) - 1]);
auto kp = tls::make_key_pair(curve);
auto kp = make_key_pair(curve);
vector<uint8_t> contents(contents_.begin(), contents_.end());
const vector<uint8_t> signature = kp->sign(contents);
auto cert = kp->self_sign("CN=name");
auto verifier = tls::make_verifier(cert);
auto verifier = make_verifier(cert);
CHECK(verifier->verify(contents, signature));
}
}
@ -189,24 +183,24 @@ TEST_CASE("Sign, verify. Fail to verify with bad contents")
for (const auto curve : supported_curves)
{
INFO("With curve: " << labels[static_cast<size_t>(curve) - 1]);
auto kp = tls::make_key_pair(curve);
auto kp = make_key_pair(curve);
vector<uint8_t> contents(contents_.begin(), contents_.end());
const vector<uint8_t> signature = kp->sign(contents);
auto cert = kp->self_sign("CN=name");
auto verifier = tls::make_verifier(cert);
auto verifier = make_verifier(cert);
CHECK(verifier->verify(contents, signature));
corrupt(contents);
CHECK_FALSE(verifier->verify(contents, signature));
}
}
tls::HashBytes bad_manual_hash(const std::vector<uint8_t>& data)
crypto::HashBytes bad_manual_hash(const std::vector<uint8_t>& data)
{
// secp256k1 requires 32-byte hashes, other curves don't care. So use 32 for
// secp256r1 requires 32-byte hashes, other curves don't care. So use 32 for
// general hasher
constexpr auto n = 32;
tls::HashBytes hash(n);
crypto::HashBytes hash(n);
for (size_t i = 0; i < data.size(); ++i)
{
@ -221,16 +215,16 @@ TEST_CASE("Manually hash, sign, verify, with PublicKey")
for (const auto curve : supported_curves)
{
INFO("With curve: " << labels[static_cast<size_t>(curve) - 1]);
auto kp = tls::make_key_pair(curve);
auto kp = make_key_pair(curve);
vector<uint8_t> contents(contents_.begin(), contents_.end());
tls::HashBytes hash = bad_manual_hash(contents);
crypto::HashBytes hash = bad_manual_hash(contents);
const vector<uint8_t> signature = kp->sign_hash(hash.data(), hash.size());
const auto public_key = kp->public_key_pem();
auto pubk = tls::make_public_key(public_key);
CHECK(pubk->verify_hash(hash, signature));
auto pubk = make_public_key(public_key);
CHECK(pubk->verify_hash(hash, signature, MDType::SHA256));
corrupt(hash);
CHECK_FALSE(pubk->verify_hash(hash, signature));
CHECK_FALSE(pubk->verify_hash(hash, signature, MDType::SHA256));
}
}
@ -239,101 +233,19 @@ TEST_CASE("Manually hash, sign, verify, with certificate")
for (const auto curve : supported_curves)
{
INFO("With curve: " << labels[static_cast<size_t>(curve) - 1]);
auto kp = tls::make_key_pair(curve);
auto kp = make_key_pair(curve);
vector<uint8_t> contents(contents_.begin(), contents_.end());
tls::HashBytes hash = bad_manual_hash(contents);
crypto::HashBytes hash = bad_manual_hash(contents);
const vector<uint8_t> signature = kp->sign_hash(hash.data(), hash.size());
auto cert = kp->self_sign("CN=name");
auto verifier = tls::make_verifier(cert);
auto verifier = make_verifier(cert);
CHECK(verifier->verify_hash(hash, signature));
corrupt(hash);
CHECK_FALSE(verifier->verify(hash, signature));
}
}
TEST_CASE("Recoverable signatures")
{
auto kp = tls::KeyPair_k1Bitcoin(MBEDTLS_ECP_DP_SECP256K1);
vector<uint8_t> contents(contents_.begin(), contents_.end());
tls::HashBytes hash = bad_manual_hash(contents);
auto signature = kp.sign_recoverable_hashed(hash);
const auto target_pem = kp.public_key_pem().str();
auto recovered = tls::PublicKey_k1Bitcoin::recover_key(signature, hash);
{
INFO("Normal recovery");
CHECK(target_pem == recovered.public_key_pem().str());
}
// NB: Incorrect arguments _may_ cause the verification to throw with no
// recoverable key, but they may simply cause a different key to be returned.
// These tests look for either type of failure.
{
INFO("Corrupted hash");
auto hash2(hash);
corrupt(hash2);
bool recovery_failed = false;
try
{
auto r = tls::PublicKey_k1Bitcoin::recover_key(signature, hash2);
recovery_failed = target_pem != r.public_key_pem().str();
}
catch (const std::exception& e)
{
recovery_failed = true;
}
CHECK(recovery_failed);
}
{
INFO("Corrupted signature");
auto signature2(signature);
corrupt(signature2.raw);
bool recovery_failed = false;
try
{
auto r = tls::PublicKey_k1Bitcoin::recover_key(signature2, hash);
recovery_failed = target_pem != r.public_key_pem().str();
}
catch (const std::exception& e)
{
recovery_failed = true;
}
CHECK(recovery_failed);
}
{
INFO("Corrupted recovery_id");
auto signature3(signature);
signature3.recovery_id = (signature3.recovery_id + 1) % 4;
bool recovery_failed = false;
try
{
auto r = tls::PublicKey_k1Bitcoin::recover_key(signature3, hash);
recovery_failed = target_pem != r.public_key_pem().str();
}
catch (const std::exception& e)
{
recovery_failed = true;
}
CHECK(recovery_failed);
}
{
INFO("Recovered key is useable");
auto norm_sig = kp.sign(contents);
CHECK(recovered.verify(contents, norm_sig));
corrupt(norm_sig);
CHECK_FALSE(recovered.verify(contents, norm_sig));
}
}
TEST_CASE("base64")
{
for (size_t length = 1; length < 20; ++length)
@ -341,8 +253,8 @@ TEST_CASE("base64")
std::vector<uint8_t> raw(length);
std::generate(raw.begin(), raw.end(), rand);
const auto encoded = tls::b64_from_raw(raw.data(), raw.size());
const auto decoded = tls::raw_from_b64(encoded);
const auto encoded = b64_from_raw(raw.data(), raw.size());
const auto decoded = raw_from_b64(encoded);
REQUIRE(decoded == raw);
}
}
@ -354,12 +266,12 @@ TEST_CASE("base64url")
std::vector<uint8_t> raw(length);
std::generate(raw.begin(), raw.end(), rand);
auto encoded = tls::b64_from_raw(raw.data(), raw.size());
auto encoded = b64_from_raw(raw.data(), raw.size());
std::replace(encoded.begin(), encoded.end(), '+', '-');
std::replace(encoded.begin(), encoded.end(), '/', '_');
encoded.erase(
std::find(encoded.begin(), encoded.end(), '='), encoded.end());
const auto decoded = tls::raw_from_b64url(encoded);
const auto decoded = raw_from_b64url(encoded);
REQUIRE(decoded == raw);
}
}
@ -367,20 +279,20 @@ TEST_CASE("base64url")
TEST_CASE("Wrap, unwrap with RSAKeyPair")
{
size_t input_len = 64;
std::vector<uint8_t> input = tls::create_entropy()->random(input_len);
std::vector<uint8_t> input = create_entropy()->random(input_len);
INFO("Cannot make RSA key from EC key");
{
auto rsa_kp = tls::make_key_pair(); // EC Key
auto rsa_kp = make_key_pair(); // EC Key
REQUIRE_THROWS_AS(
tls::make_rsa_public_key(rsa_kp->public_key_pem()), std::logic_error);
make_rsa_public_key(rsa_kp->public_key_pem()), std::logic_error);
}
INFO("Without label");
{
auto rsa_kp = tls::make_rsa_key_pair();
auto rsa_pub = tls::make_rsa_public_key(rsa_kp->public_key_pem());
auto rsa_kp = make_rsa_key_pair();
auto rsa_pub = make_rsa_public_key(rsa_kp->public_key_pem());
// Public key can wrap
auto wrapped = rsa_pub->wrap(input);
@ -398,8 +310,8 @@ TEST_CASE("Wrap, unwrap with RSAKeyPair")
INFO("With label");
{
auto rsa_kp = tls::make_rsa_key_pair();
auto rsa_pub = tls::make_rsa_public_key(rsa_kp->public_key_pem());
auto rsa_kp = make_rsa_key_pair();
auto rsa_pub = make_rsa_public_key(rsa_kp->public_key_pem());
std::string label = "my_label";
auto wrapped = rsa_pub->wrap(input, label);
auto unwrapped = rsa_kp->unwrap(wrapped, label);
@ -412,11 +324,43 @@ TEST_CASE("Extract public key from cert")
for (const auto curve : supported_curves)
{
INFO("With curve: " << labels[static_cast<size_t>(curve) - 1]);
auto kp = tls::make_key_pair(curve);
auto kp = make_key_pair(curve);
auto pk = kp->public_key_pem();
auto cert = kp->self_sign("CN=name");
auto pubk = tls::public_key_pem_from_cert(cert);
auto pubk = public_key_pem_from_cert(cert);
REQUIRE(pk == pubk);
}
}
template <typename T, typename S>
void run_csr()
{
T kpm(CurveID::SECP384R1);
const char* subject_name = "CN=myname";
auto csr = kpm.create_csr(subject_name);
std::vector<SubjectAltName> subject_alternative_names;
subject_alternative_names.push_back({"email:my-other-name", false});
subject_alternative_names.push_back({"www.microsoft.com", false});
subject_alternative_names.push_back({"192.168.0.1", true});
auto icrt = kpm.self_sign("CN=issuer");
auto crt = kpm.sign_csr(icrt, csr, subject_alternative_names);
std::vector<uint8_t> content = {0, 1, 2, 3, 4};
auto signature = kpm.sign(content);
S v(crt.raw());
REQUIRE(v.verify(content, signature));
}
TEST_CASE("Create, sign & verify certificates")
{
run_csr<KeyPair_mbedTLS, Verifier_mbedTLS>();
run_csr<KeyPair_mbedTLS, Verifier_OpenSSL>();
run_csr<KeyPair_OpenSSL, Verifier_mbedTLS>();
run_csr<KeyPair_OpenSSL, Verifier_OpenSSL>();
}

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

@ -2,116 +2,75 @@
// Licensed under the Apache 2.0 License.
#pragma once
#include "crypto/hash.h"
#include "curve.h"
#include "error_string.h"
#include "hash.h"
#include "key_pair.h"
#include "mbedtls/pem.h"
#include "pem.h"
#include "rsa_key_pair.h"
#include <mbedtls/pem.h>
#include <openssl/evp.h>
#include <openssl/x509.h>
using namespace crypto;
namespace tls
{
static constexpr size_t max_pem_cert_size = 4096;
// As these are not exposed by mbedlts, define them here to allow simple
// As these are not exposed by mbedTLS, define them here to allow simple
// conversion from DER to PEM format
static constexpr auto PEM_CERTIFICATE_HEADER =
"-----BEGIN CERTIFICATE-----\n";
static constexpr auto PEM_CERTIFICATE_FOOTER = "-----END CERTIFICATE-----\n";
class Verifier
class VerifierBase
{
protected:
mutable mbedtls::X509Crt cert;
std::shared_ptr<PublicKeyBase> public_key;
MDType md_type = MDType::NONE;
public:
/**
* Construct from a pre-parsed cert
*
* @param c Initialised and parsed x509 cert
*/
Verifier(mbedtls::X509Crt&& c) : cert(std::move(c)) {}
VerifierBase() : public_key(nullptr) {}
virtual ~VerifierBase() {}
Verifier(const Verifier&) = delete;
virtual std::vector<uint8_t> cert_der() = 0;
virtual Pem cert_pem() = 0;
virtual ~Verifier() = default;
/**
* Verify that a signature was produced on a hash with the private key
* associated with the public key contained in the certificate.
*
* @param hash First byte in hash sequence
* @param hash_size Number of bytes in hash sequence
* @param signature First byte in signature sequence
* @param signature_size Number of bytes in signature sequence
* @param md_type Digest algorithm to use. Derived from the
* public key if MBEDTLS_MD_NONE.
*
* @return Whether the signature matches the hash and the key
*/
virtual bool verify_hash(
const uint8_t* hash,
size_t hash_size,
const uint8_t* signature,
size_t signature_size,
mbedtls_md_type_t md_type = {}) const
{
if (md_type == MBEDTLS_MD_NONE)
md_type = get_md_for_ec(get_ec_from_context(cert->pk));
int rc = mbedtls_pk_verify(
&cert->pk, md_type, hash, hash_size, signature, signature_size);
if (rc)
LOG_DEBUG_FMT("Failed to verify signature: {}", error_string(rc));
return rc == 0;
}
/**
* Verify that a signature was produced on a hash with the private key
* associated with the public key contained in the certificate.
*
* @param hash Hash produced from contents as a sequence of bytes
* @param signature Signature as a sequence of bytes
* @param md_type Digest algorithm to use. Derived from the
* public key if MBEDTLS_MD_NONE.
*
* @return Whether the signature matches the hash and the key
*/
bool verify_hash(
const std::vector<uint8_t>& hash,
const std::vector<uint8_t>& signature,
mbedtls_md_type_t md_type = {}) const
{
return verify_hash(
hash.data(), hash.size(), signature.data(), signature.size(), md_type);
}
bool verify_hash(
const std::vector<uint8_t>& hash,
virtual bool verify(
const uint8_t* contents,
size_t contents_size,
const uint8_t* sig,
size_t sig_size,
mbedtls_md_type_t md_type = {}) const
MDType md_type) const
{
return verify_hash(hash.data(), hash.size(), sig, sig_size, md_type);
if (md_type == MDType::NONE)
md_type = this->md_type;
return public_key->verify(
contents, contents_size, sig, sig_size, md_type);
}
/**
* Verify that a signature was produced on contents with the private key
* associated with the public key contained in the certificate.
*
* @param contents Sequence of bytes that was signed
* @param signature Signature as a sequence of bytes
* @param md_type Digest algorithm to use. Derived from the
* public key if MBEDTLS_MD_NONE.
*
* @return Whether the signature matches the contents and the key
*/
bool verify(
virtual bool verify(
const uint8_t* contents,
size_t contents_size,
const uint8_t* sig,
size_t sig_size,
MDType md_type,
HashBytes& hash_bytes) const
{
if (md_type == MDType::NONE)
md_type = this->md_type;
return public_key->verify(
contents, contents_size, sig, sig_size, md_type, hash_bytes);
}
virtual bool verify(
const std::vector<uint8_t>& contents,
const std::vector<uint8_t>& signature,
mbedtls_md_type_t md_type = {}) const
MDType md_type = MDType::NONE) const
{
return verify(
contents.data(),
@ -121,10 +80,10 @@ namespace tls
md_type);
}
bool verify(
virtual bool verify(
const std::vector<uint8_t>& contents,
const std::vector<uint8_t>& signature,
mbedtls_md_type_t md_type,
MDType md_type,
HashBytes& hash_bytes) const
{
return verify(
@ -136,42 +95,128 @@ namespace tls
hash_bytes);
}
bool verify(
const uint8_t* contents,
size_t contents_size,
virtual bool verify_hash(
const uint8_t* hash,
size_t hash_size,
const uint8_t* sig,
size_t sig_size,
mbedtls_md_type_t md_type = {}) const
MDType md_type = MDType::NONE)
{
HashBytes hash;
do_hash(cert->pk, contents, contents_size, hash, md_type);
if (md_type == MDType::NONE)
md_type = this->md_type;
return verify_hash(hash, sig, sig_size, md_type);
return public_key->verify_hash(hash, hash_size, sig, sig_size, md_type);
}
bool verify(
const uint8_t* contents,
size_t contents_size,
const uint8_t* sig,
size_t sig_size,
mbedtls_md_type_t md_type,
HashBytes& hash_bytes) const
virtual bool verify_hash(
const std::vector<uint8_t>& hash,
const std::vector<uint8_t>& signature,
MDType md_type = MDType::NONE)
{
do_hash(cert->pk, contents, contents_size, hash_bytes, md_type);
return verify_hash(hash_bytes, sig, sig_size, md_type);
return verify_hash(
hash.data(), hash.size(), signature.data(), signature.size(), md_type);
}
template <size_t SIZE>
bool verify_hash(
const std::array<uint8_t, SIZE>& hash,
const std::vector<uint8_t>& signature,
MDType md_type = MDType::NONE)
{
return verify_hash(
hash.data(), hash.size(), signature.data(), signature.size(), md_type);
}
virtual CurveID get_curve_id() const
{
return public_key->get_curve_id();
}
};
class Verifier_mbedTLS : public VerifierBase
{
protected:
mutable mbedtls::X509Crt cert;
inline MDType get_md_type(mbedtls_md_type_t mdt) const
{
switch (mdt)
{
case MBEDTLS_MD_NONE:
return MDType::NONE;
case MBEDTLS_MD_SHA1:
return MDType::SHA1;
case MBEDTLS_MD_SHA256:
return MDType::SHA256;
case MBEDTLS_MD_SHA384:
return MDType::SHA384;
case MBEDTLS_MD_SHA512:
return MDType::SHA512;
default:
return MDType::NONE;
}
return MDType::NONE;
}
public:
/**
* Construct from a certificate
*
* @param c Certificate in DER or PEM format
*/
Verifier_mbedTLS(const std::vector<uint8_t>& c) : VerifierBase()
{
cert = mbedtls::make_unique<mbedtls::X509Crt>();
int rc = mbedtls_x509_crt_parse(cert.get(), c.data(), c.size());
if (rc)
{
throw std::invalid_argument(
fmt::format("Failed to parse certificate: {}", error_string(rc)));
}
md_type = get_md_type(cert->sig_md);
// public_key expects to have unique ownership of the context and so does
// `cert`, so we duplicate the key context here.
unsigned char buf[2048];
rc = mbedtls_pk_write_pubkey_pem(&cert->pk, buf, sizeof(buf));
if (rc != 0)
{
throw std::runtime_error(
fmt::format("PEM export failed: {}", error_string(rc)));
}
Pem pem(buf, sizeof(buf));
if (mbedtls_pk_can_do(&cert->pk, MBEDTLS_PK_ECKEY))
{
public_key = std::make_unique<PublicKey_mbedTLS>(pem);
}
else if (mbedtls_pk_can_do(&cert->pk, MBEDTLS_PK_RSA))
{
public_key = std::make_unique<RSAPublicKey_mbedTLS>(pem);
}
else
{
throw std::logic_error("unsupported public key type");
}
}
Verifier_mbedTLS(const Verifier_mbedTLS&) = delete;
virtual ~Verifier_mbedTLS() = default;
const mbedtls_x509_crt* raw()
{
return cert.get();
}
std::vector<uint8_t> der_cert_data()
virtual std::vector<uint8_t> cert_der() override
{
return {cert->raw.p, cert->raw.p + cert->raw.len};
}
Pem cert_pem()
virtual Pem cert_pem() override
{
unsigned char buf[max_pem_cert_size];
size_t len;
@ -195,70 +240,134 @@ namespace tls
}
};
class Verifier_k1Bitcoin : public Verifier
class Verifier_OpenSSL : public VerifierBase
{
protected:
BCk1ContextPtr bc_ctx = make_bc_context(SECP256K1_CONTEXT_VERIFY);
mutable X509* cert;
secp256k1_pubkey bc_pub;
public:
template <typename... Ts>
Verifier_k1Bitcoin(Ts... ts) : Verifier(std::forward<Ts>(ts)...)
MDType get_md_type(int mdt) const
{
parse_secp256k_bc(cert->pk, bc_ctx->p, &bc_pub);
switch (mdt)
{
case NID_undef:
return MDType::NONE;
case NID_sha1:
return MDType::SHA1;
case NID_sha256:
return MDType::SHA256;
case NID_sha384:
return MDType::SHA384;
case NID_sha512:
return MDType::SHA512;
default:
return MDType::NONE;
}
return MDType::NONE;
}
bool verify_hash(
const uint8_t* hash,
size_t hash_size,
const uint8_t* signature,
size_t signature_size,
mbedtls_md_type_t = {}) const override
public:
/**
* Construct from a certificate
*
* @param c Certificate in DER or PEM format
*/
Verifier_OpenSSL(const std::vector<uint8_t>& c) : VerifierBase()
{
bool ok = verify_secp256k_bc(
bc_ctx->p, signature, signature_size, hash, hash_size, &bc_pub);
BIO* certbio = BIO_new_mem_buf(c.data(), c.size());
if (!(cert = PEM_read_bio_X509(certbio, NULL, 0, NULL)))
{
BIO_reset(certbio);
if (!(cert = d2i_X509_bio(certbio, NULL)))
{
throw std::invalid_argument(fmt::format(
"OpenSSL error: {}", ERR_error_string(ERR_get_error(), NULL)));
}
}
BIO_free(certbio);
return ok;
int mdnid, pknid, secbits;
X509_get_signature_info(cert, &mdnid, &pknid, &secbits, 0);
md_type = get_md_type(mdnid);
EVP_PKEY* pk = X509_get_pubkey(cert);
if (EVP_PKEY_get0_EC_KEY(pk))
{
public_key = std::make_unique<PublicKey_OpenSSL>(pk);
}
else if (EVP_PKEY_get0_RSA(pk))
{
public_key = std::make_unique<RSAPublicKey_OpenSSL>(pk);
}
else
{
throw std::logic_error("unsupported public key type");
}
}
Verifier_OpenSSL(Verifier_OpenSSL&& v) = default;
Verifier_OpenSSL(const Verifier_OpenSSL&) = delete;
virtual ~Verifier_OpenSSL()
{
if (cert)
X509_free(cert);
}
const X509* raw()
{
return cert;
}
virtual std::vector<uint8_t> cert_der() override
{
BIO* mem = BIO_new(BIO_s_mem());
OPENSSL_CHECK1(i2d_X509_bio(mem, cert));
BUF_MEM* bptr;
BIO_get_mem_ptr(mem, &bptr);
std::vector<uint8_t> result = {(uint8_t*)bptr->data,
(uint8_t*)bptr->data + bptr->length};
BIO_free(mem);
return result;
}
virtual Pem cert_pem() override
{
BIO* mem = BIO_new(BIO_s_mem());
OPENSSL_CHECK1(PEM_write_bio_X509(mem, cert));
BUF_MEM* bptr;
BIO_get_mem_ptr(mem, &bptr);
Pem result((uint8_t*)bptr->data, bptr->length);
BIO_free(mem);
return result;
}
};
using VerifierPtr = std::shared_ptr<Verifier>;
using VerifierUniquePtr = std::unique_ptr<Verifier>;
using VerifierPtr = std::shared_ptr<VerifierBase>;
using VerifierUniquePtr = std::unique_ptr<VerifierBase>;
/**
* Construct Verifier from a certificate in DER or PEM format
*
* @param cert Sequence of bytes containing the certificate
*/
inline VerifierUniquePtr make_unique_verifier(
const std::vector<uint8_t>& cert,
bool use_bitcoin_impl = prefer_bitcoin_secp256k1)
const std::vector<uint8_t>& cert)
{
auto x509 = mbedtls::make_unique<mbedtls::X509Crt>();
int rc = mbedtls_x509_crt_parse(x509.get(), cert.data(), cert.size());
if (rc)
{
throw std::invalid_argument(
fmt::format("Failed to parse certificate: {}", error_string(rc)));
}
if (x509->pk.pk_info == mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY))
{
const auto curve = get_ec_from_context(x509->pk);
if (curve == MBEDTLS_ECP_DP_SECP256K1 && use_bitcoin_impl)
{
return std::make_unique<Verifier_k1Bitcoin>(std::move(x509));
}
}
return std::make_unique<Verifier>(std::move(x509));
#ifdef CRYPTO_PROVIDER_IS_MBEDTLS
return std::make_unique<Verifier_mbedTLS>(cert);
#else
return std::make_unique<Verifier_OpenSSL>(cert);
#endif
}
inline VerifierPtr make_verifier(
const Pem& cert, bool use_bitcoin_impl = prefer_bitcoin_secp256k1)
inline VerifierPtr make_verifier(const Pem& cert)
{
return make_unique_verifier(cert.raw(), use_bitcoin_impl);
return make_unique_verifier(cert.raw());
}
inline tls::Pem cert_der_to_pem(const std::vector<uint8_t>& der_cert_raw)
@ -268,6 +377,6 @@ namespace tls
inline std::vector<uint8_t> cert_pem_to_der(const std::string& pem_cert_raw)
{
return make_verifier(pem_cert_raw)->der_cert_data();
return make_verifier(pem_cert_raw)->cert_der();
}
}

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

@ -32,7 +32,8 @@ def run(cert_test):
test(
["--sn=CN=subject", "--san=iPAddress:1.2.3.4"],
"Subject: CN = subject\n",
"X509v3 Subject Alternative Name: \n" + 16 * " " + "IP Address:1.2.3.4",
"X509v3 Subject Alternative Name: \n" + 16 * " ",
"IP Address:1.2.3.4",
)
test(
@ -42,15 +43,16 @@ def run(cert_test):
"--san=iPAddress:192.168.200.123",
],
"Subject: CN = subject\n",
"X509v3 Subject Alternative Name: \n"
+ 16 * " "
+ "IP Address:192.168.200.123, IP Address:1.2.3.4",
"X509v3 Subject Alternative Name: \n" + 16 * " ",
"IP Address:192.168.200.123",
"IP Address:1.2.3.4",
)
test(
["--sn=CN=subject", "--san=dNSName:sub.domain.tld"],
"Subject: CN = subject\n",
"X509v3 Subject Alternative Name: \n" + 16 * " " + "DNS:sub.domain.tld",
"X509v3 Subject Alternative Name: \n" + 16 * " ",
"DNS:sub.domain.tld",
)
test(
@ -61,9 +63,10 @@ def run(cert_test):
"--san=iPAddress:192.168.200.123",
],
"Subject: CN = subject\n",
"X509v3 Subject Alternative Name: \n"
+ 16 * " "
+ "IP Address:192.168.200.123, DNS:sub.domain.tld, IP Address:1.2.3.4",
"X509v3 Subject Alternative Name: \n" + 16 * " ",
"IP Address:192.168.200.123",
"DNS:sub.domain.tld",
"IP Address:1.2.3.4",
)

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

@ -78,7 +78,7 @@ def run(args):
_,
careful_vote,
) = network.consortium.generate_and_propose_new_member(
primary, curve=infra.network.ParticipantsCurve.secp256k1
primary, curve=infra.network.ParticipantsCurve.secp256r1
)
proposals_issued += 1
@ -91,7 +91,7 @@ def run(args):
LOG.info("Create new proposal but withdraw it before it is accepted")
new_member_proposal, _, _ = network.consortium.generate_and_propose_new_member(
primary, curve=infra.network.ParticipantsCurve.secp256k1
primary, curve=infra.network.ParticipantsCurve.secp256r1
)
proposals_issued += 1

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

@ -7,11 +7,6 @@ from enum import IntEnum
import secrets
import datetime
import coincurve
from coincurve._libsecp256k1 import ffi, lib # pylint: disable=no-name-in-module
from coincurve.context import GLOBAL_CONTEXT
from cryptography.exceptions import InvalidSignature
from cryptography import x509
from cryptography.x509.oid import NameOID
from cryptography.x509 import (
@ -35,96 +30,32 @@ import jwt
RECOMMENDED_RSA_PUBLIC_EXPONENT = 65537
# As per mbedtls md_type_t
# As per tls::MDType
class CCFDigestType(IntEnum):
MD_NONE = 0
MD_MD2 = 1
MD_MD4 = 2
MD_MD5 = 3
MD_SHA1 = 4
MD_SHA224 = 5
MD_SHA256 = 6
MD_SHA384 = 7
MD_SHA512 = 8
MD_RIPEMD160 = 9
# This function calls the native API and does not rely on the
# imported library's implementation. Though not being used by
# the current test, it might still be helpful to have this
# sequence of native calls for verification, in case the
# imported library's code changes.
def verify_recover_secp256k1_bc_native(
signature, req, hasher=coincurve.utils.sha256, context=GLOBAL_CONTEXT
):
# Compact
native_rec_sig = ffi.new("secp256k1_ecdsa_recoverable_signature *")
raw_sig, recovery_id = signature[:64], coincurve.utils.bytes_to_int(signature[64:])
lib.secp256k1_ecdsa_recoverable_signature_parse_compact(
context.ctx, native_rec_sig, raw_sig, recovery_id
)
# Recover public key
native_public_key = ffi.new("secp256k1_pubkey *")
msg_hash = hasher(req) if hasher is not None else req
lib.secp256k1_ecdsa_recover(
context.ctx, native_public_key, native_rec_sig, msg_hash
)
# Convert
native_standard_sig = ffi.new("secp256k1_ecdsa_signature *")
lib.secp256k1_ecdsa_recoverable_signature_convert(
context.ctx, native_standard_sig, native_rec_sig
)
# Verify
ret = lib.secp256k1_ecdsa_verify(
context.ctx, native_standard_sig, msg_hash, native_public_key
)
return ret
def verify_recover_secp256k1_bc(
signature, req, hasher=coincurve.utils.sha256, context=GLOBAL_CONTEXT
):
msg_hash = hasher(req) if hasher is not None else req
rec_sig = coincurve.ecdsa.deserialize_recoverable(signature)
public_key = coincurve.PublicKey(coincurve.ecdsa.recover(req, rec_sig))
n_sig = coincurve.ecdsa.recoverable_convert(rec_sig)
if not lib.secp256k1_ecdsa_verify(
context.ctx, n_sig, msg_hash, public_key.public_key
):
raise RuntimeError("Failed to verify SECP256K1 bitcoin signature")
MD_SHA1 = 1
MD_SHA256 = 2
MD_SHA384 = 3
MD_SHA512 = 4
def verify_request_sig(raw_cert, sig, req, request_body, md):
try:
cert = x509.load_der_x509_certificate(raw_cert, backend=default_backend())
cert = x509.load_der_x509_certificate(raw_cert, backend=default_backend())
# Verify that the request digest matches the hash of the body
h = hashes.Hash(hashes.SHA256(), backend=default_backend())
h.update(request_body)
raw_req_digest = h.finalize()
header_digest = base64.b64decode(req.decode().split("SHA-256=")[1])
assert (
header_digest == raw_req_digest
), "Digest header does not match request body"
# Verify that the request digest matches the hash of the body
h = hashes.Hash(hashes.SHA256(), backend=default_backend())
h.update(request_body)
raw_req_digest = h.finalize()
header_digest = base64.b64decode(req.decode().split("SHA-256=")[1])
assert header_digest == raw_req_digest, "Digest header does not match request body"
pub_key = cert.public_key()
signature_hash_alg = ec.ECDSA(
hashes.SHA256()
if md == CCFDigestType.MD_SHA256
else cert.signature_hash_algorithm
)
pub_key.verify(sig, req, signature_hash_alg)
except InvalidSignature as e:
# we support a non-standard curve, which is also being
# used for bitcoin.
if pub_key._curve.name != "secp256k1": # pylint: disable=protected-access
raise e
verify_recover_secp256k1_bc(sig, req)
pub_key = cert.public_key()
signature_hash_alg = ec.ECDSA(
hashes.SHA256()
if md == CCFDigestType.MD_SHA256
else cert.signature_hash_algorithm
)
pub_key.verify(sig, req, signature_hash_alg)
def generate_aes_key(key_bits: int) -> bytes:

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

@ -33,7 +33,7 @@ class ServiceStatus(Enum):
class ParticipantsCurve(IntEnum):
secp384r1 = 0
secp256k1 = 1
secp256r1 = 1
def next(self):
return ParticipantsCurve((self.value + 1) % len(ParticipantsCurve))

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

@ -1,7 +1,6 @@
wheel
paramiko
loguru
coincurve
psutil
cimetrics>=0.2.1
pynacl