зеркало из https://github.com/microsoft/CCF.git
Родитель
4cf0b30515
Коммит
cd5792537a
|
@ -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>(
|
||||
|
|
127
src/tls/curve.h
127
src/tls/curve.h
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -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
|
||||
|
|
Загрузка…
Ссылка в новой задаче