Now that we use OpenSSL for TLS connections, we removed the remaining bits that relied on mbedTLS.
This commit is contained in:
Christoph M. Wintersteiger 2022-01-18 17:43:53 +00:00 коммит произвёл GitHub
Родитель 0e95e8c0ed
Коммит 8666bbc96f
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
91 изменённых файлов: 1306 добавлений и 4342 удалений

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

@ -1 +1 @@
Daily now!
One daily, please!

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

@ -1,22 +0,0 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the Apache 2.0 License.
find_path(MBEDTLS_INCLUDE_DIRS mbedtls/ssl.h)
find_library(MBEDTLS_LIBRARY mbedtls)
find_library(MBEDX509_LIBRARY mbedx509)
find_library(MBEDCRYPTO_LIBRARY mbedcrypto)
set(MBEDTLS_LIBRARIES "${MBEDTLS_LIBRARY}" "${MBEDX509_LIBRARY}"
"${MBEDCRYPTO_LIBRARY}"
)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(
MbedTLS DEFAULT_MSG MBEDTLS_INCLUDE_DIRS MBEDTLS_LIBRARY MBEDX509_LIBRARY
MBEDCRYPTO_LIBRARY
)
mark_as_advanced(
MBEDTLS_INCLUDE_DIRS MBEDTLS_LIBRARY MBEDX509_LIBRARY MBEDCRYPTO_LIBRARY
)

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

@ -35,20 +35,14 @@ find_package(OpenEnclave 0.17.5 CONFIG REQUIRED)
# used for our edge cases (eg - for virtual libraries). These do not follow the
# standard naming patterns, for example use OE_INCLUDEDIR rather than
# OpenEnclave_INCLUDE_DIRS
set(OE_CRYPTO_LIB
mbedtls
CACHE STRING "Crypto library used by enclaves."
)
set(OE_TARGET_LIBC openenclave::oelibc)
set(OE_TARGET_ENCLAVE_AND_STD
openenclave::oeenclave openenclave::oecryptombedtls openenclave::oelibcxx
openenclave::oelibc openenclave::oecryptoopenssl
set(OE_TARGET_ENCLAVE_AND_STD openenclave::oeenclave openenclave::oelibcxx
openenclave::oelibc openenclave::oecryptoopenssl
)
# These oe libraries must be linked in specific order
set(OE_TARGET_ENCLAVE_CORE_LIBS
openenclave::oeenclave openenclave::oecryptombedtls openenclave::oesnmalloc
openenclave::oecore openenclave::oesyscall
set(OE_TARGET_ENCLAVE_CORE_LIBS openenclave::oeenclave openenclave::oesnmalloc
openenclave::oecore openenclave::oesyscall
)
option(LVI_MITIGATIONS "Enable LVI mitigations" ON)
@ -143,15 +137,6 @@ function(enable_quote_code name)
endif()
endfunction()
function(use_client_mbedtls name)
target_include_directories(${name} PRIVATE ${CLIENT_MBEDTLS_INCLUDE_DIR})
target_link_libraries(${name} PRIVATE ${CLIENT_MBEDTLS_LIBRARIES})
endfunction()
function(use_oe_mbedtls name)
target_link_libraries(${name} PRIVATE ${OE_TARGET_ENCLAVE_AND_STD})
endfunction()
# Enclave library wrapper
function(add_ccf_app name)

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

@ -30,13 +30,6 @@ if(VERBOSE_LOGGING)
set(TEST_HOST_LOGGING_LEVEL "debug")
endif()
option(NO_STRICT_TLS_CIPHERSUITES
"Disable strict list of valid TLS ciphersuites" OFF
)
if(NO_STRICT_TLS_CIPHERSUITES)
add_compile_definitions(NO_STRICT_TLS_CIPHERSUITES)
endif()
option(USE_NULL_ENCRYPTOR "Turn off encryption of ledger updates - debug only"
OFF
)
@ -60,12 +53,6 @@ if(ENABLE_HTTP2)
add_compile_definitions(ENABLE_HTTP2)
endif()
option(TLS_PROVIDER_IS_MBEDTLS "Force TLS provider to MbedTLS" OFF)
if(TLS_PROVIDER_IS_MBEDTLS)
message(STATUS "Using MbedTLS for TLS")
add_compile_definitions(TLS_PROVIDER_IS_MBEDTLS)
endif()
option(ENABLE_BFT "Enable experimental BFT consensus at compile time" OFF)
if(ENABLE_BFT)
add_compile_definitions(ENABLE_BFT)
@ -90,11 +77,6 @@ set(CCF_3RD_PARTY_INTERNAL_DIR "${CCF_DIR}/3rdparty/internal")
include_directories(SYSTEM ${CCF_3RD_PARTY_EXPORTED_DIR})
include_directories(SYSTEM ${CCF_3RD_PARTY_INTERNAL_DIR})
find_package(MbedTLS REQUIRED)
set(CLIENT_MBEDTLS_INCLUDE_DIR "${MBEDTLS_INCLUDE_DIRS}")
set(CLIENT_MBEDTLS_LIBRARIES "${MBEDTLS_LIBRARIES}")
include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/tools.cmake)
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/cmake/tools.cmake DESTINATION cmake)
include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/ccf_app.cmake)
@ -332,7 +314,6 @@ install(
# CCF endpoints libs
add_enclave_library(ccf_endpoints.enclave "${CCF_ENDPOINTS_SOURCES}")
use_oe_mbedtls(ccf_endpoints.enclave)
add_warning_checks(ccf_endpoints.enclave)
install(
TARGETS ccf_endpoints.enclave
@ -340,7 +321,6 @@ install(
DESTINATION lib
)
add_host_library(ccf_endpoints.host "${CCF_ENDPOINTS_SOURCES}")
use_client_mbedtls(ccf_endpoints.host)
add_san(ccf_endpoints.host)
add_warning_checks(ccf_endpoints.host)
install(
@ -371,7 +351,6 @@ set(CCF_NETWORK_TEST_ARGS -l ${TEST_HOST_LOGGING_LEVEL} --worker-threads
if("sgx" IN_LIST COMPILE_TARGETS)
add_enclave_library(js_openenclave.enclave ${CCF_DIR}/src/js/openenclave.cpp)
use_oe_mbedtls(js_openenclave.enclave)
target_link_libraries(js_openenclave.enclave PUBLIC ccf.enclave)
add_lvi_mitigations(js_openenclave.enclave)
install(
@ -392,7 +371,6 @@ if("virtual" IN_LIST COMPILE_TARGETS)
set_property(
TARGET js_openenclave.virtual PROPERTY POSITION_INDEPENDENT_CODE ON
)
use_client_mbedtls(js_openenclave.virtual)
install(
TARGETS js_openenclave.virtual
EXPORT ccf
@ -404,7 +382,6 @@ if("sgx" IN_LIST COMPILE_TARGETS)
add_enclave_library(
js_generic_base.enclave ${CCF_DIR}/src/apps/js_generic/js_generic_base.cpp
)
use_oe_mbedtls(js_generic_base.enclave)
target_link_libraries(js_generic_base.enclave PUBLIC ccf.enclave)
add_lvi_mitigations(js_generic_base.enclave)
install(
@ -429,7 +406,6 @@ if("virtual" IN_LIST COMPILE_TARGETS)
set_property(
TARGET js_generic_base.virtual PROPERTY POSITION_INDEPENDENT_CODE ON
)
use_client_mbedtls(js_generic_base.virtual)
install(
TARGETS js_generic_base.virtual
EXPORT ccf

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

@ -19,7 +19,7 @@ endif()
# CPack variables for Debian packages
set(CPACK_DEBIAN_PACKAGE_DEPENDS
"open-enclave (>=0.17.5), libuv1 (>= 1.34.2), libc++1-10, libc++abi1-10"
"open-enclave (>=0.17.5), libuv1 (>= 1.34.2), libc++1-10, libc++abi1-10, openssl (>=1.1.1)"
)
set(CPACK_DEBIAN_FILE_NAME DEB-DEFAULT)

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

@ -10,25 +10,17 @@ set(CCFCRYPTO_SRC
${CCF_DIR}/src/crypto/rsa_key_pair.cpp
${CCF_DIR}/src/crypto/verifier.cpp
${CCF_DIR}/src/crypto/key_wrap.cpp
${CCF_DIR}/src/crypto/mbedtls/symmetric_key.cpp
${CCF_DIR}/src/crypto/openssl/symmetric_key.cpp
${CCF_DIR}/src/crypto/mbedtls/public_key.cpp
${CCF_DIR}/src/crypto/openssl/public_key.cpp
${CCF_DIR}/src/crypto/mbedtls/key_pair.cpp
${CCF_DIR}/src/crypto/openssl/key_pair.cpp
${CCF_DIR}/src/crypto/mbedtls/hash.cpp
${CCF_DIR}/src/crypto/openssl/hash.cpp
${CCF_DIR}/src/crypto/mbedtls/rsa_public_key.cpp
${CCF_DIR}/src/crypto/openssl/rsa_public_key.cpp
${CCF_DIR}/src/crypto/mbedtls/rsa_key_pair.cpp
${CCF_DIR}/src/crypto/openssl/rsa_key_pair.cpp
${CCF_DIR}/src/crypto/mbedtls/verifier.cpp
${CCF_DIR}/src/crypto/openssl/verifier.cpp
)
if("sgx" IN_LIST COMPILE_TARGETS)
add_enclave_library(ccfcrypto.enclave ${CCFCRYPTO_SRC})
use_oe_mbedtls(ccfcrypto.enclave)
install(
TARGETS ccfcrypto.enclave
@ -43,7 +35,6 @@ target_compile_options(ccfcrypto.host PUBLIC ${COMPILE_LIBCXX})
target_link_options(ccfcrypto.host PUBLIC ${LINK_LIBCXX})
target_link_libraries(ccfcrypto.host PUBLIC crypto)
target_link_libraries(ccfcrypto.host PUBLIC ssl)
use_client_mbedtls(ccfcrypto.host)
set_property(TARGET ccfcrypto.host PROPERTY POSITION_INDEPENDENT_CODE ON)
install(

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

@ -65,7 +65,6 @@ if(ENABLE_V8)
set_property(
TARGET js_v8_base.virtual PROPERTY POSITION_INDEPENDENT_CODE ON
)
use_client_mbedtls(js_v8_base.virtual)
install(
TARGETS js_v8_base.virtual
EXPORT ccf
@ -80,7 +79,6 @@ if(ENABLE_V8)
add_lvi_mitigations(v8_oe_stubs.enclave)
add_enclave_library(js_v8_base.enclave ${js_v8_src})
use_oe_mbedtls(js_v8_base.enclave)
target_include_directories(
js_v8_base.enclave PRIVATE ${js_v8_dir} ${v8_sgx_include_dir}
)

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

@ -95,7 +95,7 @@ This general signature also allows a handler to see additional caller context. A
:end-before: SNIPPET_END: log_record_prefix_cert
:dedent:
This uses mbedTLS to parse the caller's TLS certificate, and prefixes the logged message with the ``Subject`` field extracted from this certificate.
This parses the caller's TLS certificate, and prefixes the logged message with the ``Subject`` field extracted from this certificate.
If a handler makes no writes to the KV, it may be installed as read-only:

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

@ -45,8 +45,6 @@ The full list of build switches can be obtained by running:
The most common build switches include:
* **BUILD_TESTS**: Boolean. Build all tests for CCF. Default to ON.
* **CLIENT_MBEDTLS_PREFIX**: Path. Prefix to mbedTLS install to be used by test clients. Default to ``/usr/local``.
* **NO_STRICT_TLS_CIPHERSUITES**: Boolean. Relax the list of accepted TLS ciphersuites. Default to OFF.
* **SAN**: Boolean. Build unit tests with Address and Undefined behaviour sanitizers enabled. Default to OFF.
* **COMPILE_TARGETS**: String. List of target compilation platforms. Defaults to ``sgx;virtual``, which builds both "virtual" enclaves and actual SGX enclaves.
* **VERBOSE_LOGGING**: Boolean. Enable all logging levels. Default to OFF.

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

@ -34,42 +34,6 @@
become: yes
when: "'moby-buildx' not in ansible_facts.packages"
- name: Download mbedtls
get_url:
url: https://github.com/ARMmbed/mbedtls/archive/{{ mbedtls_src }}
dest: "{{ workspace }}/{{ mbedtls_src }}"
become: true
- name: Remove existing mbedtls checkout
file:
path: "{{ workspace }}/mbedtls-{{ mbedtls_dir }}"
state: absent
- name: Expand mbedtls
unarchive:
src: "{{ workspace }}/{{ mbedtls_src }}"
dest: "{{ workspace }}"
copy: no
creates: "{{ workspace }}/mbedtls-{{ mbedtls_dir }}/CMakeLists.txt"
- name: Make mbedtls build dir
file:
path: "{{ workspace }}/mbedtls-{{ mbedtls_dir }}/build"
state: directory
- name: Build mbedtls
shell: |
cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release -DCMAKE_POSITION_INDEPENDENT_CODE=ON ..
make
args:
chdir: "{{ workspace }}/mbedtls-{{ mbedtls_dir }}/build"
- name: Install mbedtls
command: make install
args:
chdir: "{{ workspace }}/mbedtls-{{ mbedtls_dir }}/build"
become: true
- name: Remove doxygen debian package
apt:
name: doxygen

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

@ -31,10 +31,6 @@ debs:
docker_debs:
- docker-ce-cli
mbedtls_ver: "2.16.10"
mbedtls_dir: "mbedtls-{{ mbedtls_ver }}"
mbedtls_src: "{{ mbedtls_dir }}.tar.gz"
doxygen_ver: "1.9.3"
doxygen_bin: "doxygen-{{ doxygen_ver }}.linux.bin.tar.gz"
doxygen_url: "https://doxygen.nl/files/{{ doxygen_bin }}"

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

@ -13,10 +13,7 @@
#include "ccf/indexing/seqnos_by_key.h"
#include "ccf/user_frontend.h"
#include "ccf/version.h"
// FIXME: The header below is used for a single check of the certificate and
// could be done using OpenSSL. For now, we keep it as is, but we should make
// the change once we deprecate, and remove, mbedTLS.
#include "crypto/mbedtls/mbedtls_wrappers.h"
#include "crypto/verifier.h"
#include <charconv>
#define FMT_HEADER_ONLY
@ -439,12 +436,13 @@ namespace loggingapp
return;
}
auto cert = mbedtls::make_unique<mbedtls::X509Crt>();
const auto& cert_data = ctx.rpc_ctx->session->caller_cert;
const auto ret = mbedtls_x509_crt_parse(
cert.get(), cert_data.data(), cert_data.size());
if (ret != 0)
std::shared_ptr<Verifier> verifier;
try
{
const auto& cert_data = ctx.rpc_ctx->session->caller_cert;
verifier = make_verifier(cert_data);
}
catch (const std::exception& ex)
{
ctx.rpc_ctx->set_error(
HTTP_STATUS_INTERNAL_SERVER_ERROR,
@ -453,7 +451,8 @@ namespace loggingapp
return;
}
const auto log_line = fmt::format("{}: {}", cert->subject, in.msg);
const auto log_line =
fmt::format("{}: {}", verifier->subject(), in.msg);
auto records_handle = ctx.tx.template rw<RecordsMap>(PRIVATE_RECORDS);
records_handle->put(in.id, log_line);
update_first_write(ctx.tx, in.id);

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

@ -1,70 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the Apache 2.0 License.
#pragma once
#include "crypto/mbedtls/error_string.h"
#include "crypto/mbedtls/mbedtls_wrappers.h"
#include "crypto/pem.h"
#include "ds/buffer.h"
#include <exception>
// This is a copy of src/tls/ca.h
// Moving tls to OpenSSL means this different client implementation needs
// to be isolated while we change tls_endpoint. Once that's done, we can
// come back here and refactor this too.
namespace client::tls
{
enum TlsAuth
{
tls_auth_default,
tls_auth_none,
tls_auth_optional,
tls_auth_required
};
class TlsCA
{
private:
crypto::mbedtls::X509Crt ca = nullptr;
crypto::mbedtls::X509Crl crl = nullptr;
public:
TlsCA(CBuffer ca_ = nullb, CBuffer crl_ = nullb)
{
auto tmp_ca = crypto::mbedtls::make_unique<crypto::mbedtls::X509Crt>();
auto tmp_crl = crypto::mbedtls::make_unique<crypto::mbedtls::X509Crl>();
if (ca_.n > 0)
{
crypto::Pem pem_ca(ca_);
auto ret =
mbedtls_x509_crt_parse(tmp_ca.get(), pem_ca.data(), pem_ca.size());
if (ret != 0)
throw std::logic_error(
"Could not parse TlsCA: " + crypto::error_string(ret));
}
if (crl_.n > 0)
{
crypto::Pem pem_crl(crl_);
auto ret =
mbedtls_x509_crl_parse(tmp_crl.get(), pem_crl.data(), pem_crl.size());
if (ret != 0)
throw std::logic_error(
"Could not parse CRL: " + crypto::error_string(ret));
}
ca = std::move(tmp_ca);
crl = std::move(tmp_crl);
}
~TlsCA() {}
void use(mbedtls_ssl_config* cfg)
{
mbedtls_ssl_conf_ca_chain(cfg, ca.get(), crl.get());
}
};
}

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

@ -1,147 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the Apache 2.0 License.
#pragma once
#include "ca.h"
#include "crypto/mbedtls/error_string.h"
#include "crypto/mbedtls/mbedtls_wrappers.h"
#include <cstring>
#include <memory>
#include <optional>
// This is a copy of src/tls/cert.h
// Moving tls to OpenSSL means this different client implementation needs
// to be isolated while we change tls_endpoint. Once that's done, we can
// come back here and refactor this too.
using namespace crypto;
namespace client::tls
{
// This class represents the authentication/authorization context for a TLS
// session. At least, it contains the peer's CA. At most, it also contains our
// own private key/certificate which will be presented in the TLS handshake.
// The peer's certificate verification can be overridden with the auth
// parameter.
class TlsCert
{
private:
std::shared_ptr<TlsCA> peer_ca;
std::optional<std::string> peer_hostname;
mbedtls::X509Crt own_cert = nullptr;
mbedtls::PKContext own_pkey = nullptr;
bool has_own_cert;
TlsAuth auth;
public:
TlsCert(
std::shared_ptr<TlsCA> peer_ca_,
const std::optional<crypto::Pem>& own_cert_ = std::nullopt,
const std::optional<crypto::Pem>& own_pkey_ = std::nullopt,
CBuffer pw = nullb,
TlsAuth tls_auth_ = tls_auth_default,
const std::optional<std::string>& peer_hostname_ = std::nullopt) :
peer_ca(peer_ca_),
peer_hostname(peer_hostname_),
has_own_cert(false),
auth(tls_auth_)
{
auto tmp_cert = mbedtls::make_unique<mbedtls::X509Crt>();
auto tmp_pkey = mbedtls::make_unique<mbedtls::PKContext>();
if (own_cert_.has_value() && own_pkey_.has_value())
{
int rc = mbedtls_x509_crt_parse(
tmp_cert.get(), own_cert_->data(), own_cert_->size());
if (rc != 0)
{
throw std::logic_error(
"Could not parse certificate: " + error_string(rc));
}
rc = mbedtls_pk_parse_key(
tmp_pkey.get(), own_pkey_->data(), own_pkey_->size(), pw.p, pw.n);
if (rc != 0)
{
throw std::logic_error("Could not parse key: " + error_string(rc));
}
has_own_cert = true;
}
own_cert = std::move(tmp_cert);
own_pkey = std::move(tmp_pkey);
}
~TlsCert() {}
void use(mbedtls_ssl_context* ssl, mbedtls_ssl_config* cfg)
{
if (peer_hostname.has_value())
{
// Peer hostname is only checked against peer certificate (SAN
// extension) if it is set. This lets us connect to peers that present
// certificates with IPAddress in SAN field (mbedtls does not parse
// IPAddress in SAN field). This is OK since we check for peer CA
// endorsement.
mbedtls_ssl_set_hostname(ssl, peer_hostname->c_str());
}
if (peer_ca)
{
peer_ca->use(cfg);
}
if (auth != tls_auth_default)
{
mbedtls_ssl_conf_authmode(cfg, authmode(auth));
}
if (has_own_cert)
{
mbedtls_ssl_conf_own_cert(cfg, own_cert.get(), own_pkey.get());
}
}
const mbedtls_x509_crt* raw()
{
return own_cert.get();
}
private:
int authmode(TlsAuth auth)
{
switch (auth)
{
case tls_auth_none:
{
// Peer certificate is not checked
return MBEDTLS_SSL_VERIFY_NONE;
}
case tls_auth_optional:
{
// Peer certificate is checked but handshake continues even if
// verification fails
return MBEDTLS_SSL_VERIFY_OPTIONAL;
}
case tls_auth_required:
{
// Peer must present a valid certificate
return MBEDTLS_SSL_VERIFY_REQUIRED;
}
default:
{
}
}
return MBEDTLS_SSL_VERIFY_REQUIRED;
}
};
}

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

@ -105,8 +105,8 @@ namespace client
HttpRpcTlsClient(
const std::string& host,
const std::string& port,
std::shared_ptr<tls::TlsCA> node_ca = nullptr,
std::shared_ptr<tls::TlsCert> cert = nullptr,
std::shared_ptr<tls::CA> node_ca = nullptr,
std::shared_ptr<tls::Cert> cert = nullptr,
const std::string& key_id_ = "Invalid") :
TlsClient(host, port, node_ca, cert),
parser(*this),

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

@ -2,26 +2,66 @@
// Licensed under the Apache 2.0 License.
#pragma once
#include "ca.h"
#include "cert.h"
#include "crypto/mbedtls/error_string.h"
#include "crypto/openssl/openssl_wrappers.h"
#include "ds/buffer.h"
#include "ds/logger.h"
#include "tls/ca.h"
#include "tls/cert.h"
#include <cstdint>
#include <cstring>
#include <iostream>
#include <mbedtls/config.h>
#include <mbedtls/ctr_drbg.h>
#include <mbedtls/entropy.h>
#include <mbedtls/error.h>
#include <mbedtls/net_sockets.h>
#include <mbedtls/ssl.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <openssl/bio.h>
#include <string>
#include <vector>
using namespace crypto;
using namespace crypto::OpenSSL;
#ifdef _DEBUG
static BIO* bio_err = NULL;
static void apps_ssl_info_callback(const SSL* s, int where, int ret)
{
const char* str;
int w = where & ~SSL_ST_MASK;
if (w & SSL_ST_CONNECT)
str = "SSL_connect";
else if (w & SSL_ST_ACCEPT)
str = "SSL_accept";
else
str = "undefined";
if (where & SSL_CB_LOOP)
{
BIO_printf(bio_err, "%s:%s\n", str, SSL_state_string_long(s));
}
else if (where & SSL_CB_ALERT)
{
str = (where & SSL_CB_READ) ? "read" : "write";
BIO_printf(
bio_err,
"SSL3 alert %s:%s:%s\n",
str,
SSL_alert_type_string_long(ret),
SSL_alert_desc_string_long(ret));
}
else if (where & SSL_CB_EXIT)
{
if (ret == 0)
{
BIO_printf(bio_err, "%s:failed in %s\n", str, SSL_state_string_long(s));
}
else if (ret < 0)
{
BIO_printf(bio_err, "%s:error in %s\n", str, SSL_state_string_long(s));
}
}
}
#endif
namespace client
{
@ -30,98 +70,65 @@ namespace client
protected:
std::string host;
std::string port;
std::shared_ptr<tls::TlsCA> node_ca;
std::shared_ptr<tls::TlsCert> cert;
std::shared_ptr<tls::CA> node_ca;
std::shared_ptr<tls::Cert> cert;
bool connected = false;
mbedtls::NetContext server_fd;
mbedtls::Entropy entropy;
mbedtls::CtrDrbg ctr_drbg;
mbedtls::SSLContext ssl;
mbedtls::SSLConfig conf;
Unique_SSL_CTX ctx;
Unique_BIO bio;
void init()
{
auto tmp_server_fd = mbedtls::make_unique<mbedtls::NetContext>();
auto tmp_entropy = mbedtls::make_unique<mbedtls::Entropy>();
auto tmp_ctr_drbg = mbedtls::make_unique<mbedtls::CtrDrbg>();
auto tmp_ssl = mbedtls::make_unique<mbedtls::SSLContext>();
auto tmp_conf = mbedtls::make_unique<mbedtls::SSLConfig>();
SSL_CTX_clear_mode(ctx, SSL_MODE_AUTO_RETRY);
auto err = mbedtls_ctr_drbg_seed(
tmp_ctr_drbg.get(),
mbedtls_entropy_func,
tmp_entropy.get(),
nullptr,
0);
if (err)
throw std::logic_error(crypto::error_string(err));
err = mbedtls_net_connect(
tmp_server_fd.get(), host.c_str(), port.c_str(), MBEDTLS_NET_PROTO_TCP);
if (err)
throw std::logic_error(crypto::error_string(err));
err = mbedtls_ssl_config_defaults(
tmp_conf.get(),
MBEDTLS_SSL_IS_CLIENT,
MBEDTLS_SSL_TRANSPORT_STREAM,
MBEDTLS_SSL_PRESET_DEFAULT);
if (err)
throw std::logic_error(crypto::error_string(err));
if (cert != nullptr)
cert->use(tmp_ssl.get(), tmp_conf.get());
if (node_ca != nullptr)
node_ca->use(tmp_conf.get());
mbedtls_ssl_conf_rng(
tmp_conf.get(), mbedtls_ctr_drbg_random, tmp_ctr_drbg.get());
mbedtls_ssl_conf_authmode(tmp_conf.get(), MBEDTLS_SSL_VERIFY_REQUIRED);
err = mbedtls_ssl_setup(tmp_ssl.get(), tmp_conf.get());
if (err)
throw std::logic_error(crypto::error_string(err));
if (err)
throw std::logic_error(crypto::error_string(err));
mbedtls_ssl_set_bio(
tmp_ssl.get(),
tmp_server_fd.get(),
mbedtls_net_send,
mbedtls_net_recv,
nullptr);
while (true)
SSL* ssl;
BIO_get_ssl(bio, &ssl);
if (!ssl)
{
err = mbedtls_ssl_handshake(tmp_ssl.get());
if (err == 0)
break;
if (
(err != MBEDTLS_ERR_SSL_WANT_READ) &&
(err != MBEDTLS_ERR_SSL_WANT_WRITE))
throw std::logic_error(crypto::error_string(err));
throw std::runtime_error("Couldn't locate SSL pointer");
}
connected = true;
SSL_clear_mode(ssl, SSL_MODE_AUTO_RETRY);
server_fd = std::move(tmp_server_fd);
entropy = std::move(tmp_entropy);
ctr_drbg = std::move(tmp_ctr_drbg);
ssl = std::move(tmp_ssl);
conf = std::move(tmp_conf);
#ifdef _DEBUG
bio_err = BIO_new_fp(stdout, BIO_NOCLOSE);
SSL_CTX_set_info_callback(ctx, apps_ssl_info_callback);
SSL_set_info_callback(ssl, apps_ssl_info_callback);
#endif
BIO_set_conn_hostname(bio, host.c_str());
BIO_set_conn_port(bio, port.c_str());
BIO_set_nbio(bio, 1);
if (cert)
cert->use(ssl, ctx);
if (node_ca)
node_ca->use(ctx);
do
{
BIO_do_connect(bio);
} while (BIO_should_retry(bio));
do
{
BIO_do_handshake(bio);
} while (BIO_should_retry(bio));
connected = true;
}
public:
TlsClient(
const std::string& host,
const std::string& port,
std::shared_ptr<tls::TlsCA> node_ca = nullptr,
std::shared_ptr<tls::TlsCert> cert = nullptr) :
std::shared_ptr<tls::CA> node_ca = nullptr,
std::shared_ptr<tls::Cert> cert = nullptr) :
host(host),
port(port),
node_ca(node_ca),
cert(cert)
cert(cert),
ctx(TLS_client_method()),
bio(ctx)
{
init();
}
@ -130,39 +137,53 @@ namespace client
host(c.host),
port(c.port),
node_ca(c.node_ca),
cert(c.cert)
cert(c.cert),
ctx(TLS_client_method()),
bio(ctx)
{
init();
}
virtual ~TlsClient()
{
// Signal the end of the connection
if (connected)
mbedtls_ssl_close_notify(ssl.get());
}
virtual ~TlsClient() {}
auto get_ciphersuite_name()
{
return mbedtls_ssl_get_ciphersuite(ssl.get());
SSL* ssl;
BIO_get_ssl(bio, &ssl);
return SSL_CIPHER_get_name(SSL_get_current_cipher(ssl));
}
void write(CBuffer b)
{
for (size_t written = 0; written < b.n;)
{
auto ret = mbedtls_ssl_write(ssl.get(), b.p + written, b.n - written);
if (ret > 0)
auto ret = 0;
do
{
ret = BIO_write(bio, b.p + written, b.n - written);
} while (ret < 0 && BIO_should_retry(bio));
if (ret >= 0)
{
written += ret;
}
else
throw std::logic_error(crypto::error_string(ret));
{
throw std::logic_error(error_string(ERR_get_error()));
}
}
}
std::vector<uint8_t> read(size_t read_size)
{
std::vector<uint8_t> buf(read_size);
auto ret = mbedtls_ssl_read(ssl.get(), buf.data(), buf.size());
auto ret = 0;
do
{
ret = BIO_read(bio, buf.data(), buf.size());
} while (ret < 0 && BIO_should_retry(bio));
if (ret > 0)
{
buf.resize(ret);
@ -174,7 +195,7 @@ namespace client
}
else
{
throw std::logic_error(crypto::error_string(ret));
throw std::logic_error(error_string(ERR_get_error()));
}
return buf;
@ -182,14 +203,19 @@ namespace client
bool bytes_available()
{
return mbedtls_ssl_get_bytes_avail(ssl.get()) > 0;
return BIO_pending(bio) > 0;
}
std::vector<uint8_t> read_all()
{
constexpr auto read_size = 4096;
std::vector<uint8_t> buf(read_size);
auto ret = mbedtls_ssl_read(ssl.get(), buf.data(), buf.size());
auto ret = 0;
do
{
ret = BIO_read(bio, buf.data(), buf.size());
} while (ret < 0 && BIO_should_retry(bio));
if (ret > 0)
{
buf.resize(ret);
@ -201,7 +227,7 @@ namespace client
}
else
{
throw std::logic_error(crypto::error_string(ret));
throw std::logic_error(error_string(ERR_get_error()));
}
return buf;
@ -210,8 +236,9 @@ namespace client
void set_tcp_nodelay(bool on)
{
int option = on ? 1 : 0;
setsockopt(
server_fd->fd, IPPROTO_TCP, TCP_NODELAY, (char*)&option, sizeof(int));
int fd = -1;
BIO_get_fd(bio, &fd);
setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char*)&option, sizeof(int));
}
};
}

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

@ -4,12 +4,12 @@
#include "ccf/entity_id.h"
#include "consensus/consensus_types.h"
#include "crypto/ecdsa.h"
#include "crypto/hash.h"
#include "ds/ring_buffer_types.h"
#include "enclave/rpc_context.h"
#include "enclave/rpc_handler.h"
#include "kv/kv_types.h"
#include "mbedtls/ecdsa.h"
#include <array>
#include <chrono>
@ -151,15 +151,6 @@ namespace aft
AppendEntriesResponseType success;
};
struct SignedAppendEntriesResponse : RaftHeader
{
Term term;
Index last_log_idx;
Nonce hashed_nonce;
uint32_t signature_size;
std::array<uint8_t, MBEDTLS_ECDSA_MAX_LEN> sig;
};
struct SignaturesReceivedAck : RaftHeader
{
Term term;
@ -202,4 +193,4 @@ namespace aft
bool vote_granted;
};
#pragma pack(pop)
}
}

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

@ -1,18 +1,13 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the Apache 2.0 License.
#include "mbedtls/base64.h"
#include "openssl/base64.h"
#include "ds/logger.h"
#include "openssl/base64.h"
namespace crypto
{
#ifdef CRYPTO_PROVIDER_IS_MBEDTLS
using Base64Impl = Base64_mbedtls;
#else
using Base64Impl = Base64_openssl;
#endif
std::vector<uint8_t> raw_from_b64(const std::string_view& b64_string)
{

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

@ -3,7 +3,7 @@
#include "entropy.h"
#include "mbedtls/entropy.h"
#include "openssl/entropy.h"
namespace crypto
{
@ -16,6 +16,6 @@ namespace crypto
return intel_drng_ptr;
}
return std::make_shared<Entropy_mbedTLS>();
return std::make_shared<Entropy_OpenSSL>();
}
}

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

@ -35,9 +35,6 @@ namespace crypto
Entropy() = default;
virtual ~Entropy() = default;
virtual void* get_data() = 0;
virtual rng_func_t get_rng() = 0;
/// Generate @p len random bytes
/// @param len Number of random bytes to generate
/// @return vector random bytes
@ -328,16 +325,6 @@ namespace crypto
return 0;
}
rng_func_t get_rng() override
{
return &rng;
}
void* get_data() override
{
return this;
}
static bool is_drng_supported()
{
return (get_drng_support() & (DRNG_HAS_RDRAND | DRNG_HAS_RDSEED)) ==

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

@ -2,7 +2,6 @@
// Licensed under the Apache 2.0 License.
#include "hash.h"
#include "mbedtls/hash.h"
#include "openssl/hash.h"
#include <openssl/sha.h>
@ -48,10 +47,6 @@ namespace crypto
const std::vector<uint8_t>& salt,
const std::vector<uint8_t>& info)
{
#if defined(CRYPTO_PROVIDER_IS_MBEDTLS)
return mbedtls::hkdf(md_type, length, ikm, salt, info);
#else
return OpenSSL::hkdf(md_type, length, ikm, salt, info);
#endif
}
}

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

@ -4,16 +4,15 @@
#include "crypto/entropy.h"
#include "crypto/key_pair.h"
#include "crypto/mbedtls/error_string.h"
#include "crypto/mbedtls/key_pair.h"
#include "crypto/openssl/openssl_wrappers.h"
#include "crypto/openssl/public_key.h"
#include "ds/logger.h"
#include <iostream>
#include <map>
#include <mbedtls/ecdh.h>
#include <mbedtls/ecp.h>
#include <mbedtls/pk.h>
#include <openssl/crypto.h>
#include <openssl/ec.h>
#include <openssl/ossl_typ.h>
#include <stdexcept>
namespace tls
@ -21,182 +20,102 @@ namespace tls
class KeyExchangeContext
{
private:
crypto::mbedtls::ECDHContext ctx = nullptr;
std::vector<uint8_t> key_share;
std::vector<uint8_t> peer_key_share;
void create_fresh_key_share()
{
auto tmp_ctx =
crypto::mbedtls::make_unique<crypto::mbedtls::ECDHContext>();
size_t len = 0;
int rc = mbedtls_ecp_group_load(&tmp_ctx->grp, domain_parameter);
if (rc != 0)
{
throw std::logic_error(crypto::error_string(rc));
}
crypto::EntropyPtr entropy = crypto::create_entropy();
key_share.resize(len_public);
rc = mbedtls_ecdh_make_public(
tmp_ctx.get(),
&len,
key_share.data(),
key_share.size(),
entropy->get_rng(),
entropy->get_data());
if (rc != 0)
{
throw std::logic_error(crypto::error_string(rc));
}
key_share.resize(len);
ctx = std::move(tmp_ctx);
}
crypto::KeyPairPtr own_key;
crypto::PublicKeyPtr peer_key;
crypto::CurveID curve;
public:
static constexpr mbedtls_ecp_group_id domain_parameter =
MBEDTLS_ECP_DP_SECP384R1;
static constexpr size_t len_public = 1024 + 1;
static constexpr size_t len_shared_secret = 1024;
KeyExchangeContext() : key_share(len_public)
KeyExchangeContext() : curve(crypto::CurveID::SECP384R1)
{
create_fresh_key_share();
own_key = make_key_pair(curve);
}
KeyExchangeContext(
std::shared_ptr<crypto::KeyPair_mbedTLS> own_kp,
std::shared_ptr<crypto::PublicKey_mbedTLS> peer_pubk)
std::shared_ptr<crypto::KeyPair> own_kp,
std::shared_ptr<crypto::PublicKey> peer_pubk) :
curve(own_kp->get_curve_id())
{
auto tmp_ctx =
crypto::mbedtls::make_unique<crypto::mbedtls::ECDHContext>();
int rc = mbedtls_ecdh_get_params(
tmp_ctx.get(),
mbedtls_pk_ec(*own_kp->get_raw_context()),
MBEDTLS_ECDH_OURS);
if (rc != 0)
{
throw std::logic_error(crypto::error_string(rc));
}
rc = mbedtls_ecdh_get_params(
tmp_ctx.get(),
mbedtls_pk_ec(*peer_pubk->get_raw_context()),
MBEDTLS_ECDH_THEIRS);
if (rc != 0)
{
throw std::logic_error(crypto::error_string(rc));
}
ctx = std::move(tmp_ctx);
own_key = own_kp;
peer_key = peer_pubk;
}
void free_ctx()
{
// Should only be called when shared secret has been computed.
ctx.reset();
}
~KeyExchangeContext() {}
~KeyExchangeContext()
std::vector<uint8_t> get_own_key_share() const
{
free_ctx();
OPENSSL_cleanse(key_share.data(), key_share.size());
}
const std::vector<uint8_t>& get_own_key_share() const
{
if (!ctx)
if (!own_key)
{
throw std::runtime_error("Missing key exchange context");
throw std::runtime_error("missing node key");
}
if (key_share.empty())
{
throw std::runtime_error("Missing node key share");
}
// Note that this function returns a vector of bytes
// where the first byte represents the
// size of the public key
return key_share;
// For backwards compatibility we need to keep the format we used with
// mbedTLS, which is the raw EC point with an extra size byte in the
// front.
auto tmp = own_key->public_key_raw();
tmp.insert(tmp.begin(), tmp.size());
return tmp;
}
const std::vector<uint8_t>& get_peer_key_share() const
std::vector<uint8_t> get_peer_key_share() const
{
return peer_key_share;
if (!peer_key)
{
throw std::runtime_error("missing peer key");
}
auto tmp = peer_key->public_key_raw();
tmp.insert(tmp.begin(), tmp.size());
return tmp;
}
void reset()
{
key_share.clear();
peer_key_share.clear();
ctx.reset();
create_fresh_key_share();
peer_key.reset();
own_key = make_key_pair(crypto::CurveID::SECP384R1);
}
void load_peer_key_share(const std::vector<uint8_t>& ks)
{
load_peer_key_share({ks.data(), ks.size()});
if (ks.size() == 0)
{
throw std::runtime_error("missing peer key share");
}
auto tmp = ks;
tmp.erase(tmp.begin());
int nid = crypto::PublicKey_OpenSSL::get_openssl_group_id(curve);
auto pk = crypto::key_from_raw_ec_point(tmp, nid);
if (!pk)
{
throw std::runtime_error("could not parse peer key share");
}
peer_key = std::make_shared<crypto::PublicKey_OpenSSL>(pk);
}
void load_peer_key_share(CBuffer ks)
{
if (!ctx)
{
throw std::runtime_error(
"Missing key exchange context when loading peer key share");
}
if (ks.n == 0)
{
throw std::runtime_error("Missing peer key share");
}
peer_key_share = {ks.p, ks.p + ks.n};
load_peer_key_share({ks.p, ks.p + ks.n});
}
std::vector<uint8_t> compute_shared_secret()
{
int rc;
if (peer_key_share.size() > 0)
if (!own_key)
{
rc = mbedtls_ecdh_read_public(
ctx.get(), peer_key_share.data(), peer_key_share.size());
if (rc != 0)
{
throw std::logic_error(crypto::error_string(rc));
}
throw std::logic_error("missing own key");
}
crypto::EntropyPtr entropy = crypto::create_entropy();
// Should only be called once, when peer public has been loaded.
std::vector<uint8_t> shared_secret(len_shared_secret);
size_t len;
rc = mbedtls_ecdh_calc_secret(
ctx.get(),
&len,
shared_secret.data(),
shared_secret.size(),
entropy->get_rng(),
entropy->get_data());
if (rc != 0)
if (!peer_key)
{
throw std::logic_error(crypto::error_string(rc));
throw std::logic_error("missing peer key");
}
shared_secret.resize(len);
return shared_secret;
auto r = own_key->derive_shared_secret(*peer_key);
own_key.reset();
peer_key.reset();
return r;
}
};
}

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

@ -3,8 +3,6 @@
#include "key_pair.h"
#include "mbedtls/key_pair.h"
#include "mbedtls/public_key.h"
#include "openssl/key_pair.h"
#include "openssl/public_key.h"
@ -16,13 +14,8 @@
namespace crypto
{
#ifdef CRYPTO_PROVIDER_IS_MBEDTLS
using PublicKeyImpl = PublicKey_mbedTLS;
using KeyPairImpl = KeyPair_mbedTLS;
#else
using PublicKeyImpl = PublicKey_OpenSSL;
using KeyPairImpl = KeyPair_OpenSSL;
#endif
PublicKeyPtr make_public_key(const Pem& pem)
{

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

@ -23,6 +23,7 @@ namespace crypto
virtual Pem private_key_pem() const = 0;
virtual Pem public_key_pem() const = 0;
virtual std::vector<uint8_t> public_key_der() const = 0;
virtual std::vector<uint8_t> private_key_der() const = 0;
virtual bool verify(
const std::vector<uint8_t>& contents,
@ -87,6 +88,13 @@ namespace crypto
auto csr = create_csr(subject_name, subject_alt_names);
return sign_csr(Pem(0), csr, ca, valid_from, valid_to);
}
virtual std::vector<uint8_t> derive_shared_secret(
const PublicKey& peer_key) = 0;
virtual std::vector<uint8_t> public_key_raw() const = 0;
virtual CurveID get_curve_id() const = 0;
};
using PublicKeyPtr = std::shared_ptr<PublicKey>;

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

@ -1,79 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the Apache 2.0 License.
#pragma once
#include "ds/logger.h"
#include "error_string.h"
#include <mbedtls/base64.h>
#include <string>
#include <vector>
namespace crypto
{
struct Base64_mbedtls
{
static std::vector<uint8_t> raw_from_b64(const std::string_view& b64_string)
{
size_t len_written = 0;
const auto data = reinterpret_cast<const uint8_t*>(b64_string.data());
const auto size = b64_string.size();
// Obtain the size of the output buffer
auto rc = mbedtls_base64_decode(nullptr, 0, &len_written, data, size);
if (rc < 0 && rc != MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL)
{
throw std::logic_error(fmt::format(
"MBED: Could not obtain length of decoded base64 buffer: {}",
error_string(rc)));
}
std::vector<uint8_t> decoded(len_written);
rc = mbedtls_base64_decode(
decoded.data(), decoded.size(), &len_written, data, size);
if (rc != 0)
{
throw std::invalid_argument(fmt::format(
"MBED: Could not decode base64 string: {}", error_string(rc)));
}
return decoded;
}
static std::string b64_from_raw(const uint8_t* data, size_t size)
{
size_t len_written = 0;
// Obtain required size for output buffer
auto rc = mbedtls_base64_encode(nullptr, 0, &len_written, data, size);
if (rc < 0 && rc != MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL)
{
throw std::logic_error(fmt::format(
"MBED: Could not obtain length required for encoded base64 buffer: "
"{}",
error_string(rc)));
}
std::string b64_string(len_written, '\0');
auto dest = reinterpret_cast<uint8_t*>(b64_string.data());
rc = mbedtls_base64_encode(
dest, b64_string.size(), &len_written, data, size);
if (rc != 0)
{
throw std::logic_error(fmt::format(
"MBED: Could not encode base64 string: {}", error_string(rc)));
}
if (b64_string.size() > 0)
{
// mbedtls includes the terminating null, but std-string provides this
// already
b64_string.pop_back();
}
return b64_string;
}
};
}

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

@ -1,45 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the Apache 2.0 License.
#pragma once
#include "crypto/curve.h"
#include "crypto/hash.h"
#include <mbedtls/ecp.h>
#include <mbedtls/pk.h>
#include <stdexcept>
#include <string>
namespace crypto
{
// Helper to access elliptic curve id from context
inline mbedtls_ecp_group_id get_mbedtls_ec_from_context(
const mbedtls_pk_context& ctx)
{
return mbedtls_pk_ec(ctx)->grp.id;
}
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_SECP256R1:
return MBEDTLS_MD_SHA256;
default:
{
if (allow_none)
{
return MBEDTLS_MD_NONE;
}
else
{
const auto error = fmt::format("Unhandled ecp group id: {}", ec);
throw std::logic_error(error);
}
}
}
}
}

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

@ -1,75 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the Apache 2.0 License.
#pragma once
#include "crypto/entropy.h"
#include "mbedtls_wrappers.h"
#include <functional>
#include <memory>
#include <vector>
namespace crypto
{
class Entropy_mbedTLS : public Entropy
{
private:
mbedtls::Entropy entropy = mbedtls::make_unique<mbedtls::Entropy>();
mbedtls::CtrDrbg drbg = mbedtls::make_unique<mbedtls::CtrDrbg>();
static bool gen(uint64_t& v);
public:
Entropy_mbedTLS()
{
mbedtls_ctr_drbg_seed(
drbg.get(), mbedtls_entropy_func, entropy.get(), nullptr, 0);
}
std::vector<uint8_t> random(size_t len) override
{
std::vector<uint8_t> data(len);
if (mbedtls_ctr_drbg_random(drbg.get(), data.data(), data.size()) != 0)
throw std::logic_error("Couldn't create random data");
return data;
}
uint64_t random64() override
{
uint64_t rnd;
uint64_t len = sizeof(uint64_t);
if (
mbedtls_ctr_drbg_random(
drbg.get(), reinterpret_cast<unsigned char*>(&rnd), len) != 0)
{
throw std::logic_error("Couldn't create random data");
}
return rnd;
}
void random(unsigned char* data, size_t len) override
{
if (mbedtls_ctr_drbg_random(drbg.get(), data, len) != 0)
throw std::logic_error("Couldn't create random data");
}
static int rng(void* ctx, unsigned char* output, size_t len)
{
return mbedtls_ctr_drbg_random(ctx, output, len);
}
rng_func_t get_rng() override
{
return &rng;
}
void* get_data() override
{
return drbg.get();
}
};
}

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

@ -1,23 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the Apache 2.0 License.
#pragma once
#include <mbedtls/error.h>
#include <string>
namespace crypto
{
inline std::string error_string(int err)
{
constexpr size_t len = 256;
char buf[len];
mbedtls_strerror(err, buf, len);
if (strlen(buf) == 0)
{
return std::to_string(err);
}
return std::string(buf);
}
}

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

@ -1,93 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the Apache 2.0 License.
#include "hash.h"
#include "ds/buffer.h"
#include "mbedtls_wrappers.h"
#include <mbedtls/sha256.h>
#include <stdexcept>
// Note: Older system packages of mbedTLS may not support HKDF; if this include
// file is not found, consider upgrading your mbedTLS.
#include <mbedtls/hkdf.h>
namespace crypto
{
namespace mbedtls
{
std::vector<uint8_t> hkdf(
MDType md_type,
size_t length,
const std::vector<uint8_t>& ikm,
const std::vector<uint8_t>& salt,
const std::vector<uint8_t>& info)
{
auto md = mbedtls_md_info_from_type(get_md_type(md_type));
std::vector<uint8_t> okm(length);
int rc = mbedtls_hkdf(
md,
salt.data(),
salt.size(),
ikm.data(),
ikm.size(),
info.data(),
info.size(),
okm.data(),
okm.size());
return okm;
}
}
using namespace mbedtls;
void mbedtls_sha256(const CBuffer& data, uint8_t* h)
{
mbedtls_sha256_context ctx;
mbedtls_sha256_init(&ctx);
mbedtls_sha256_starts_ret(&ctx, 0);
mbedtls_sha256_update_ret(&ctx, data.p, data.rawSize());
mbedtls_sha256_finish_ret(&ctx, h);
mbedtls_sha256_free(&ctx);
}
ISha256MbedTLS::ISha256MbedTLS()
{
ctx = new mbedtls_sha256_context();
mbedtls_sha256_starts_ret((mbedtls_sha256_context*)ctx, 0);
}
ISha256MbedTLS::~ISha256MbedTLS()
{
delete (mbedtls_sha256_context*)ctx;
}
Sha256Hash ISha256MbedTLS::finalise()
{
if (!ctx)
{
throw std::logic_error("Attempting to use hash after it was finalised");
}
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());
}
}

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

@ -1,78 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the Apache 2.0 License.
#pragma once
#include "crypto/hash_provider.h"
#include "ds/buffer.h"
#include <mbedtls/md.h>
#include <mbedtls/pk.h>
#define FMT_HEADER_ONLY
#include <fmt/format.h>
namespace crypto
{
namespace mbedtls
{
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;
}
std::vector<uint8_t> hkdf(
MDType md_type,
size_t length,
const std::vector<uint8_t>& ikm,
const std::vector<uint8_t>& salt = {},
const std::vector<uint8_t>& info = {});
}
class MBedHashProvider : public HashProvider
{
public:
virtual HashBytes Hash(const uint8_t* data, size_t size, MDType type) const
{
HashBytes r;
const auto mbedtls_md_type = mbedtls::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 ISha256MbedTLS : public ISha256Hash
{
public:
ISha256MbedTLS();
~ISha256MbedTLS();
virtual void update_hash(CBuffer data);
virtual Sha256Hash finalise();
protected:
void* ctx;
};
void mbedtls_sha256(const CBuffer& data, uint8_t* h);
}

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

@ -1,357 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the Apache 2.0 License.
#include "key_pair.h"
#include "curve.h"
#include "ds/net.h"
#include "entropy.h"
#include "error_string.h"
#include "hash.h"
#define FMT_HEADER_ONLY
#include <fmt/format.h>
#include <iomanip>
#include <limits>
#include <mbedtls/asn1write.h>
#include <mbedtls/bignum.h>
#include <mbedtls/error.h>
#include <mbedtls/md.h>
#include <mbedtls/oid.h>
#include <mbedtls/pem.h>
#include <mbedtls/pk.h>
#include <mbedtls/x509.h>
#include <mbedtls/x509_crt.h>
#include <memory>
#include <string>
namespace crypto
{
using namespace mbedtls;
static 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;
}
KeyPair_mbedTLS::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::KeyPair_mbedTLS(const Pem& pem, CBuffer pw)
{
// 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));
}
}
KeyPair_mbedTLS::KeyPair_mbedTLS(mbedtls::PKContext&& k) :
PublicKey_mbedTLS(std::move(k))
{}
Pem KeyPair_mbedTLS::private_key_pem() const
{
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);
}
Pem KeyPair_mbedTLS::public_key_pem() const
{
return PublicKey_mbedTLS::public_key_pem();
}
std::vector<uint8_t> KeyPair_mbedTLS::public_key_der() const
{
return PublicKey_mbedTLS::public_key_der();
}
bool KeyPair_mbedTLS::verify(
const std::vector<uint8_t>& contents, const std::vector<uint8_t>& signature)
{
return PublicKey_mbedTLS::verify(contents, signature);
}
bool KeyPair_mbedTLS::verify(
const uint8_t* contents,
size_t contents_size,
const uint8_t* signature,
size_t signature_size)
{
return PublicKey_mbedTLS::verify(
contents, contents_size, signature, signature_size);
}
std::vector<uint8_t> KeyPair_mbedTLS::sign(CBuffer d, 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());
}
int KeyPair_mbedTLS::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);
}
std::vector<uint8_t> KeyPair_mbedTLS::sign_hash(
const uint8_t* hash, size_t hash_size) const
{
std::vector<uint8_t> sig(MBEDTLS_ECDSA_MAX_LEN);
size_t written = sizeof(sig);
if (sign_hash(hash, hash_size, &written, sig.data()) != 0)
{
return {};
}
sig.resize(written);
return sig;
}
static int ecdsa_sign_nondet(
mbedtls_pk_context* ctx,
const uint8_t* hash,
size_t hash_size,
uint8_t* sig,
size_t* sig_size)
{
EntropyPtr entropy = create_entropy();
mbedtls_ecdsa_context* ecdsa_ctx = (mbedtls_ecdsa_context*)ctx->pk_ctx;
mbedtls_mpi sr, ss;
mbedtls_mpi_init(&sr);
mbedtls_mpi_init(&ss);
int r = mbedtls_ecdsa_sign(
&ecdsa_ctx->grp,
&sr,
&ss,
&ecdsa_ctx->d,
hash,
hash_size,
entropy->get_rng(),
entropy->get_data());
unsigned char buf[MBEDTLS_ECDSA_MAX_LEN];
unsigned char* p = buf + sizeof(buf);
size_t len = 0;
len += mbedtls_asn1_write_mpi(&p, buf, &ss);
len += mbedtls_asn1_write_mpi(&p, buf, &sr);
len += mbedtls_asn1_write_len(&p, buf, len);
len += mbedtls_asn1_write_tag(
&p, buf, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
memcpy(sig, p, len);
*sig_size = len;
mbedtls_mpi_free(&sr);
mbedtls_mpi_free(&ss);
return 0;
}
int KeyPair_mbedTLS::sign_hash(
const uint8_t* hash, size_t hash_size, size_t* sig_size, uint8_t* sig) const
{
EntropyPtr entropy = create_entropy();
const auto mmdt = get_md_type(get_md_for_ec(get_curve_id()));
#ifdef DETERMINISTIC_ECDSA
return mbedtls_pk_sign(
ctx.get(),
mmdt,
hash,
hash_size,
sig,
sig_size,
entropy->get_rng(),
entropy->get_data());
#else
return ecdsa_sign_nondet(ctx.get(), hash, hash_size, sig, sig_size);
#endif
}
Pem KeyPair_mbedTLS::create_csr(
const std::string& subject_name,
const std::vector<SubjectAltName>& subject_alt_names) const
{
// mbedtls does not support parsing x509v3 extensions from a CSR
// (https://github.com/ARMmbed/mbedtls/issues/2912) so disallow CSR creation
// if any SAN is specified (use OpenSSL implementation instead)
if (!subject_alt_names.empty())
{
throw std::logic_error("mbedtls cannot create CSR with SAN");
}
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(), subject_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);
}
static void MCHK(int rc)
{
if (rc != 0)
{
throw std::logic_error(
fmt::format("mbedTLS error: {}", error_string(rc)));
}
}
Pem KeyPair_mbedTLS::sign_csr(
const Pem& issuer_cert,
const Pem& signing_request,
bool ca,
const std::optional<std::string>& valid_from,
const std::optional<std::string>& valid_to) const
{
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()));
// Verify self-signed CSR
const auto info = mbedtls_md_info_from_type(csr->sig_md);
const auto hash_size = mbedtls_md_get_size(info);
HashBytes h(hash_size);
MCHK(mbedtls_md(info, csr->cri.p, csr->cri.len, h.data()));
MCHK(mbedtls_pk_verify(
&csr->pk, csr->sig_md, h.data(), h.size(), csr->sig.p, csr->sig.len));
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
// Note: For the mbedtls implementation, we do not check that valid_from and
// valid_to are valid or chronological. See OpenSSL equivalent call for a
// safer implementation.
MCHK(mbedtls_x509write_crt_set_validity(
crt.get(),
valid_from.value_or("20210311000000").c_str(),
valid_to.value_or("20230611235959").c_str()));
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()));
// Warn: Because mbedtls does not support parsing x509v3 extensions from a
// CSR (https://github.com/ARMmbed/mbedtls/issues/2912), so those are
// ignored and not set in the certificate
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);
}
}

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

@ -1,63 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the Apache 2.0 License.
#pragma once
#include "../key_pair.h"
#include "../san.h"
#include "mbedtls_wrappers.h"
#include "public_key.h"
namespace crypto
{
class KeyPair_mbedTLS : public PublicKey_mbedTLS, public KeyPair
{
public:
KeyPair_mbedTLS(CurveID cid);
KeyPair_mbedTLS(const Pem& pem, CBuffer pw = nullb);
KeyPair_mbedTLS(mbedtls::PKContext&& k);
KeyPair_mbedTLS(const KeyPair_mbedTLS&) = delete;
virtual ~KeyPair_mbedTLS() = default;
virtual Pem private_key_pem() const override;
virtual Pem public_key_pem() const override;
virtual std::vector<uint8_t> public_key_der() const override;
using PublicKey_mbedTLS::verify;
virtual bool verify(
const std::vector<uint8_t>& contents,
const std::vector<uint8_t>& signature) override;
virtual bool verify(
const uint8_t* contents,
size_t contents_size,
const uint8_t* signature,
size_t signature_size) override;
virtual std::vector<uint8_t> sign(
CBuffer d, MDType md_type = {}) const override;
int sign(
CBuffer d, size_t* sig_size, uint8_t* sig, MDType md_type = {}) const;
std::vector<uint8_t> sign_hash(
const uint8_t* hash, size_t hash_size) const override;
virtual int sign_hash(
const uint8_t* hash,
size_t hash_size,
size_t* sig_size,
uint8_t* sig) const override;
virtual Pem create_csr(
const std::string& subject_name,
const std::vector<SubjectAltName>& subject_alt_names) const override;
virtual Pem sign_csr(
const Pem& issuer_cert,
const Pem& signing_request,
bool ca = false,
const std::optional<std::string>& valid_from = std::nullopt,
const std::optional<std::string>& valid_to = std::nullopt) const override;
};
}

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

@ -1,95 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the Apache 2.0 License.
#pragma once
#include <mbedtls/ctr_drbg.h>
#include <mbedtls/entropy.h>
#include <mbedtls/error.h>
#include <mbedtls/gcm.h>
#include <mbedtls/net_sockets.h>
#include <mbedtls/sha256.h>
#include <mbedtls/ssl.h>
#include <mbedtls/x509.h>
#include <mbedtls/x509_crt.h>
#include <mbedtls/x509_csr.h>
#include <memory>
#include <string>
namespace crypto
{
namespace mbedtls
{
template <typename T>
T make_unique();
#define DEFINE_MBEDTLS_WRAPPER( \
NEW_TYPE, MBED_TYPE, MBED_INIT_FN, MBED_FREE_FN) \
struct NEW_TYPE##Deleter \
{ \
void operator()(MBED_TYPE* ptr) \
{ \
MBED_FREE_FN(ptr); \
delete ptr; \
} \
}; \
using NEW_TYPE = std::unique_ptr<MBED_TYPE, NEW_TYPE##Deleter>; \
template <> \
inline NEW_TYPE make_unique<NEW_TYPE>() \
{ \
auto p = new MBED_TYPE; \
MBED_INIT_FN(p); \
return NEW_TYPE(p); \
}
DEFINE_MBEDTLS_WRAPPER(
CtrDrbg,
mbedtls_ctr_drbg_context,
mbedtls_ctr_drbg_init,
mbedtls_ctr_drbg_free);
DEFINE_MBEDTLS_WRAPPER(
ECDHContext, mbedtls_ecdh_context, mbedtls_ecdh_init, mbedtls_ecdh_free);
DEFINE_MBEDTLS_WRAPPER(
Entropy,
mbedtls_entropy_context,
mbedtls_entropy_init,
mbedtls_entropy_free);
DEFINE_MBEDTLS_WRAPPER(
GcmContext, mbedtls_gcm_context, mbedtls_gcm_init, mbedtls_gcm_free);
DEFINE_MBEDTLS_WRAPPER(
MPI, mbedtls_mpi, mbedtls_mpi_init, mbedtls_mpi_free);
DEFINE_MBEDTLS_WRAPPER(
NetContext, mbedtls_net_context, mbedtls_net_init, mbedtls_net_free);
DEFINE_MBEDTLS_WRAPPER(
PKContext, mbedtls_pk_context, mbedtls_pk_init, mbedtls_pk_free);
DEFINE_MBEDTLS_WRAPPER(
SSLConfig,
mbedtls_ssl_config,
mbedtls_ssl_config_init,
mbedtls_ssl_config_free);
DEFINE_MBEDTLS_WRAPPER(
SSLContext, mbedtls_ssl_context, mbedtls_ssl_init, mbedtls_ssl_free);
DEFINE_MBEDTLS_WRAPPER(
X509Crl, mbedtls_x509_crl, mbedtls_x509_crl_init, mbedtls_x509_crl_free);
DEFINE_MBEDTLS_WRAPPER(
X509Crt, mbedtls_x509_crt, mbedtls_x509_crt_init, mbedtls_x509_crt_free);
DEFINE_MBEDTLS_WRAPPER(
X509Csr, mbedtls_x509_csr, mbedtls_x509_csr_init, mbedtls_x509_csr_free);
DEFINE_MBEDTLS_WRAPPER(
X509WriteCrt,
mbedtls_x509write_cert,
mbedtls_x509write_crt_init,
mbedtls_x509write_crt_free);
DEFINE_MBEDTLS_WRAPPER(
X509WriteCsr,
mbedtls_x509write_csr,
mbedtls_x509write_csr_init,
mbedtls_x509write_csr_free);
DEFINE_MBEDTLS_WRAPPER(
SHA256Ctx,
mbedtls_sha256_context,
mbedtls_sha256_init,
mbedtls_sha256_free);
#undef DEFINE_MBEDTLS_WRAPPER
}
}

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

@ -1,157 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the Apache 2.0 License.
#include "curve.h"
#include "ds/net.h"
#include "entropy.h"
#include "error_string.h"
#include "hash.h"
#include "key_pair.h"
#define FMT_HEADER_ONLY
#include <fmt/format.h>
#include <iomanip>
#include <limits>
#include <mbedtls/asn1write.h>
#include <mbedtls/bignum.h>
#include <mbedtls/error.h>
#include <mbedtls/oid.h>
#include <mbedtls/pem.h>
#include <mbedtls/pk.h>
#include <mbedtls/x509.h>
#include <mbedtls/x509_crt.h>
#include <memory>
#include <string>
namespace crypto
{
using namespace mbedtls;
PublicKey_mbedTLS::PublicKey_mbedTLS() {}
PublicKey_mbedTLS::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()));
}
}
PublicKey_mbedTLS::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)));
}
}
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;
}
CurveID PublicKey_mbedTLS::get_curve_id() const
{
return crypto::get_curve_id(ctx.get());
}
PublicKey_mbedTLS::PublicKey_mbedTLS(mbedtls::PKContext&& c) :
ctx(std::move(c))
{}
bool PublicKey_mbedTLS::verify(
const uint8_t* contents,
size_t contents_size,
const uint8_t* sig,
size_t sig_size,
MDType md_type,
HashBytes& bytes)
{
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);
}
bool PublicKey_mbedTLS::verify_hash(
const uint8_t* hash,
size_t hash_size,
const uint8_t* sig,
size_t sig_size,
MDType md_type)
{
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;
}
Pem PublicKey_mbedTLS::public_key_pem() const
{
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);
}
std::vector<uint8_t> PublicKey_mbedTLS::public_key_der() const
{
uint8_t data[max_der_key_size];
int len = mbedtls_pk_write_pubkey_der(ctx.get(), data, max_der_key_size);
if (len < 0)
{
throw std::logic_error(
"mbedtls_pk_write_pubkey_der: " + error_string(len));
}
return {data + max_der_key_size - len, data + max_der_key_size};
};
mbedtls_pk_context* PublicKey_mbedTLS::get_raw_context() const
{
return ctx.get();
}
}

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

@ -1,51 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the Apache 2.0 License.
#pragma once
#include "../public_key.h"
#include "../san.h"
#include "mbedtls_wrappers.h"
namespace crypto
{
class PublicKey_mbedTLS : public PublicKey
{
protected:
mbedtls::PKContext ctx = mbedtls::make_unique<mbedtls::PKContext>();
PublicKey_mbedTLS();
CurveID get_curve_id() const;
public:
static constexpr size_t max_pem_key_size = 2048;
static constexpr size_t max_der_key_size = 2048;
PublicKey_mbedTLS(PublicKey_mbedTLS&& pk) = default;
PublicKey_mbedTLS(mbedtls::PKContext&& c);
PublicKey_mbedTLS(const Pem& pem);
PublicKey_mbedTLS(const std::vector<uint8_t>& der);
virtual ~PublicKey_mbedTLS() = default;
using PublicKey::verify;
using PublicKey::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;
virtual bool verify_hash(
const uint8_t* hash,
size_t hash_size,
const uint8_t* sig,
size_t sig_size,
MDType md_type) override;
virtual Pem public_key_pem() const override;
virtual std::vector<uint8_t> public_key_der() const override;
mbedtls_pk_context* get_raw_context() const;
};
}

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

@ -1,162 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the Apache 2.0 License.
#include "rsa_key_pair.h"
#include "crypto/mbedtls/hash.h"
#include "crypto/mbedtls/rsa_public_key.h"
#include "entropy.h"
#include "error_string.h"
#include "hash.h"
#include "mbedtls_wrappers.h"
namespace crypto
{
using namespace mbedtls;
RSAKeyPair_mbedTLS::RSAKeyPair_mbedTLS(
size_t public_key_size, size_t public_exponent)
{
EntropyPtr entropy = create_entropy();
int rc =
mbedtls_pk_setup(ctx.get(), mbedtls_pk_info_from_type(MBEDTLS_PK_RSA));
if (rc != 0)
{
throw std::logic_error(
"Could not set up RSA context: " + error_string(rc));
}
rc = mbedtls_rsa_gen_key(
mbedtls_pk_rsa(*ctx.get()),
entropy->get_rng(),
entropy->get_data(),
public_key_size,
public_exponent);
if (rc != 0)
{
throw std::logic_error(
"Could not generate RSA keypair: " + error_string(rc));
}
}
RSAKeyPair_mbedTLS::RSAKeyPair_mbedTLS(mbedtls::PKContext&& k) :
RSAPublicKey_mbedTLS(std::move(k))
{}
RSAKeyPair_mbedTLS::RSAKeyPair_mbedTLS(const Pem& pem, CBuffer pw) :
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));
}
}
size_t RSAKeyPair_mbedTLS::key_size() const
{
return RSAPublicKey_mbedTLS::key_size();
}
std::vector<uint8_t> RSAKeyPair_mbedTLS::rsa_oaep_unwrap(
const std::vector<uint8_t>& input,
const std::optional<std::vector<std::uint8_t>>& label)
{
mbedtls_rsa_context* rsa_ctx = mbedtls_pk_rsa(*ctx.get());
mbedtls_rsa_set_padding(rsa_ctx, rsa_padding_mode, rsa_padding_digest_id);
std::vector<uint8_t> output_buf(rsa_ctx->len);
auto entropy = create_entropy();
const unsigned char* label_ = NULL;
size_t label_size = 0;
if (label.has_value())
{
if (label->empty())
{
throw std::logic_error("empty wrapping label");
}
label_ = reinterpret_cast<const unsigned char*>(label->data());
label_size = label->size();
}
size_t olen;
auto rc = mbedtls_rsa_rsaes_oaep_decrypt(
rsa_ctx,
entropy->get_rng(),
entropy->get_data(),
MBEDTLS_RSA_PRIVATE,
label_,
label_size,
&olen,
input.data(),
output_buf.data(),
output_buf.size());
if (rc != 0)
{
throw std::logic_error(
fmt::format("Error during RSA OEAP unwrap: {}", error_string(rc)));
}
output_buf.resize(olen);
return output_buf;
}
Pem RSAKeyPair_mbedTLS::private_key_pem() const
{
unsigned char data[max_pem_key_size];
int rc = mbedtls_pk_write_key_pem(ctx.get(), data, sizeof(data));
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);
}
Pem RSAKeyPair_mbedTLS::public_key_pem() const
{
return PublicKey_mbedTLS::public_key_pem();
}
std::vector<uint8_t> RSAKeyPair_mbedTLS::public_key_der() const
{
return PublicKey_mbedTLS::public_key_der();
}
std::vector<uint8_t> RSAKeyPair_mbedTLS::sign(CBuffer d, MDType md_type) const
{
std::vector<uint8_t> r(2048);
auto hash = MBedHashProvider().Hash(d.p, d.n, md_type);
EntropyPtr entropy = create_entropy();
size_t olen = r.size();
int rc = mbedtls_pk_sign(
ctx.get(),
get_md_type(md_type),
hash.data(),
hash.size(),
r.data(),
&olen,
entropy->get_rng(),
entropy->get_data());
r.resize(olen);
return r;
}
bool RSAKeyPair_mbedTLS::verify(
const uint8_t* contents,
size_t contents_size,
const uint8_t* signature,
size_t signature_size,
MDType md_type)
{
return RSAPublicKey_mbedTLS::verify(
contents, contents_size, signature, signature_size, md_type);
}
}

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

@ -1,48 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the Apache 2.0 License.
#pragma once
#include "crypto/rsa_key_pair.h"
#include "mbedtls_wrappers.h"
#include "rsa_public_key.h"
#include <optional>
#include <vector>
namespace crypto
{
class RSAKeyPair_mbedTLS : public RSAPublicKey_mbedTLS, public RSAKeyPair
{
public:
RSAKeyPair_mbedTLS(
size_t public_key_size = default_public_key_size,
size_t public_exponent = default_public_exponent);
RSAKeyPair_mbedTLS(mbedtls::PKContext&& k);
RSAKeyPair_mbedTLS(const RSAKeyPair&) = delete;
RSAKeyPair_mbedTLS(const Pem& pem, CBuffer pw = nullb);
virtual size_t key_size() const override;
virtual ~RSAKeyPair_mbedTLS() = default;
virtual std::vector<uint8_t> rsa_oaep_unwrap(
const std::vector<uint8_t>& input,
const std::optional<std::vector<std::uint8_t>>& label =
std::nullopt) override;
virtual Pem private_key_pem() const override;
virtual Pem public_key_pem() const override;
virtual std::vector<uint8_t> public_key_der() const override;
virtual std::vector<uint8_t> sign(
CBuffer d, MDType md_type = MDType::NONE) const override;
virtual bool verify(
const uint8_t* contents,
size_t contents_size,
const uint8_t* signature,
size_t signature_size,
MDType md_type = MDType::NONE) override;
};
}

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

@ -1,123 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the Apache 2.0 License.
#include "rsa_public_key.h"
#include "crypto/mbedtls/curve.h"
#include "crypto/mbedtls/hash.h"
#include "entropy.h"
#include "error_string.h"
#include "mbedtls_wrappers.h"
#include <mbedtls/md.h>
namespace crypto
{
using namespace mbedtls;
RSAPublicKey_mbedTLS::RSAPublicKey_mbedTLS(mbedtls::PKContext&& c) :
PublicKey_mbedTLS(std::move(c))
{}
RSAPublicKey_mbedTLS::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");
}
}
RSAPublicKey_mbedTLS::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");
}
}
size_t RSAPublicKey_mbedTLS::key_size() const
{
return mbedtls_rsa_get_len(mbedtls_pk_rsa(*ctx.get())) * 8;
}
std::vector<uint8_t> RSAPublicKey_mbedTLS::rsa_oaep_wrap(
const uint8_t* input,
size_t input_size,
const uint8_t* label,
size_t label_size)
{
mbedtls_rsa_context* rsa_ctx = mbedtls_pk_rsa(*ctx.get());
mbedtls_rsa_set_padding(rsa_ctx, rsa_padding_mode, rsa_padding_digest_id);
std::vector<uint8_t> output_buf(rsa_ctx->len);
auto entropy = create_entropy();
// Note that the maximum input size to wrap is k - 2*hLen - 2
// where hLen is the hash size (32 bytes = SHA256) and
// k the wrapping key modulus size (e.g. 256 bytes = 2048 bits).
// In this example, it would be 190 bytes (1520 bits) max.
// This is enough for wrapping AES keys for example.
auto rc = mbedtls_rsa_rsaes_oaep_encrypt(
rsa_ctx,
entropy->get_rng(),
entropy->get_data(),
MBEDTLS_RSA_PUBLIC,
label,
label_size,
input_size,
input,
output_buf.data());
if (rc != 0)
{
throw std::logic_error(
fmt::format("Error during RSA OEAP wrap: {}", error_string(rc)));
}
return output_buf;
}
std::vector<uint8_t> RSAPublicKey_mbedTLS::rsa_oaep_wrap(
const std::vector<uint8_t>& input,
const std::optional<std::vector<std::uint8_t>>& label)
{
const unsigned char* label_ = NULL;
size_t label_size = 0;
if (label.has_value())
{
if (label->empty())
{
throw std::logic_error("empty wrapping label");
}
label_ = reinterpret_cast<const unsigned char*>(label->data());
label_size = label->size();
}
return rsa_oaep_wrap(input.data(), input.size(), label_, label_size);
}
Pem RSAPublicKey_mbedTLS::public_key_pem() const
{
return PublicKey_mbedTLS::public_key_pem();
}
std::vector<uint8_t> RSAPublicKey_mbedTLS::public_key_der() const
{
return PublicKey_mbedTLS::public_key_der();
}
bool RSAPublicKey_mbedTLS::verify(
const uint8_t* contents,
size_t contents_size,
const uint8_t* signature,
size_t signature_size,
MDType md_type)
{
auto hash = MBedHashProvider().Hash(contents, contents_size, md_type);
auto mbed_md = get_md_type(md_type);
int rc = mbedtls_pk_verify(
ctx.get(), mbed_md, hash.data(), hash.size(), signature, signature_size);
return rc == 0;
}
}

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

@ -1,53 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the Apache 2.0 License.
#pragma once
#include "crypto/rsa_public_key.h"
#include "mbedtls_wrappers.h"
#include "public_key.h"
#include <optional>
#include <string>
#include <vector>
namespace crypto
{
class RSAPublicKey_mbedTLS : public PublicKey_mbedTLS, public RSAPublicKey
{
public:
// Compatible with Azure HSM encryption schemes (see
// https://docs.microsoft.com/en-gb/azure/key-vault/keys/about-keys#wrapkeyunwrapkey-encryptdecrypt)
static constexpr auto rsa_padding_mode = MBEDTLS_RSA_PKCS_V21;
static constexpr auto rsa_padding_digest_id = MBEDTLS_MD_SHA256;
RSAPublicKey_mbedTLS() = default;
virtual ~RSAPublicKey_mbedTLS() = default;
RSAPublicKey_mbedTLS(mbedtls::PKContext&& c);
RSAPublicKey_mbedTLS(const Pem& pem);
RSAPublicKey_mbedTLS(const std::vector<uint8_t>& der);
virtual size_t key_size() const override;
virtual std::vector<uint8_t> rsa_oaep_wrap(
const uint8_t* input,
size_t input_size,
const uint8_t* label = nullptr,
size_t label_size = 0) override;
virtual std::vector<uint8_t> rsa_oaep_wrap(
const std::vector<uint8_t>& input,
const std::optional<std::vector<std::uint8_t>>& label =
std::nullopt) override;
virtual Pem public_key_pem() const override;
virtual std::vector<uint8_t> public_key_der() const override;
virtual bool verify(
const uint8_t* contents,
size_t contents_size,
const uint8_t* signature,
size_t signature_size,
MDType md_type = MDType::NONE) override;
};
}

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

@ -1,107 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the Apache 2.0 License.
#include "symmetric_key.h"
#include "error_string.h"
#include <mbedtls/aes.h>
#include <mbedtls/error.h>
#include <mbedtls/pk.h>
#include <stdexcept>
namespace crypto
{
using namespace mbedtls;
KeyAesGcm_mbedTLS::KeyAesGcm_mbedTLS(CBuffer rawKey)
{
for (uint32_t i = 0; i < ctxs.size(); ++i)
{
ctxs[i] = mbedtls::make_unique<mbedtls::GcmContext>();
size_t n_bits;
const auto n = static_cast<unsigned int>(rawKey.rawSize() * 8);
if (n >= 256)
{
n_bits = 256;
}
else if (n >= 192)
{
n_bits = 192;
}
else if (n >= 128)
{
n_bits = 128;
}
else
{
throw std::logic_error(
fmt::format("Need at least {} bits, only have {}", 128, n));
}
int rc = mbedtls_gcm_setkey(
ctxs[i].get(), MBEDTLS_CIPHER_ID_AES, rawKey.p, n_bits);
if (rc != 0)
{
throw std::logic_error(error_string(rc));
}
}
}
size_t KeyAesGcm_mbedTLS::key_size() const
{
auto ctx = ctxs[threading::get_current_thread_id()].get();
return ctx->cipher_ctx.key_bitlen;
}
void KeyAesGcm_mbedTLS::encrypt(
CBuffer iv,
CBuffer plain,
CBuffer aad,
uint8_t* cipher,
uint8_t tag[GCM_SIZE_TAG]) const
{
auto ctx = ctxs[threading::get_current_thread_id()].get();
int rc = mbedtls_gcm_crypt_and_tag(
ctx,
MBEDTLS_GCM_ENCRYPT,
plain.n,
iv.p,
iv.n,
aad.p,
aad.n,
plain.p,
cipher,
GCM_SIZE_TAG,
tag);
if (rc != 0)
{
throw std::logic_error(error_string(rc));
}
}
bool KeyAesGcm_mbedTLS::decrypt(
CBuffer iv,
const uint8_t tag[GCM_SIZE_TAG],
CBuffer cipher,
CBuffer aad,
uint8_t* plain) const
{
auto ctx = ctxs[threading::get_current_thread_id()].get();
return !mbedtls_gcm_auth_decrypt(
ctx,
cipher.n,
iv.p,
iv.n,
aad.p,
aad.n,
tag,
GCM_SIZE_TAG,
cipher.p,
plain);
}
}

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

@ -1,39 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the Apache 2.0 License.
#pragma once
#include "crypto/symmetric_key.h"
#include "mbedtls_wrappers.h"
namespace crypto
{
class KeyAesGcm_mbedTLS : public KeyAesGcm
{
private:
mutable std::
array<mbedtls::GcmContext, threading::ThreadMessaging::max_num_threads>
ctxs;
public:
KeyAesGcm_mbedTLS(CBuffer rawKey);
KeyAesGcm_mbedTLS(const KeyAesGcm_mbedTLS& that) = delete;
KeyAesGcm_mbedTLS(KeyAesGcm_mbedTLS&& that);
virtual ~KeyAesGcm_mbedTLS() = default;
virtual void encrypt(
CBuffer iv,
CBuffer plain,
CBuffer aad,
uint8_t* cipher,
uint8_t tag[GCM_SIZE_TAG]) const override;
virtual bool decrypt(
CBuffer iv,
const uint8_t tag[GCM_SIZE_TAG],
CBuffer cipher,
CBuffer aad,
uint8_t* plain) const override;
virtual size_t key_size() const override;
};
}

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

@ -1,220 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the Apache 2.0 License.
#include "verifier.h"
#include "error_string.h"
#include "public_key.h"
#include "rsa_key_pair.h"
#include <mbedtls/pem.h>
#include <mbedtls/x509_crt.h>
namespace crypto
{
using namespace mbedtls;
static constexpr size_t max_pem_cert_size = 4096;
// 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";
static inline std::string to_x509_time_string(const mbedtls_x509_time& time)
{
// Returns ASN1 time string (YYYYMMDDHHMMSSZ)
return fmt::format(
"{:02}{:02}{:02}{:02}{:02}{:02}Z",
time.year,
time.mon,
time.day,
time.hour,
time.min,
time.sec);
}
MDType Verifier_mbedTLS::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;
}
Verifier_mbedTLS::Verifier_mbedTLS(const std::vector<uint8_t>& c) : Verifier()
{
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)));
}
if (cert.get()->next != nullptr)
{
throw std::invalid_argument(
"PEM string contains more than one certificate");
}
// 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");
}
}
std::vector<uint8_t> Verifier_mbedTLS::cert_der()
{
return {cert->raw.p, cert->raw.p + cert->raw.len};
}
Pem Verifier_mbedTLS::cert_pem()
{
unsigned char buf[max_pem_cert_size];
size_t len;
auto rc = mbedtls_pem_write_buffer(
PEM_CERTIFICATE_HEADER,
PEM_CERTIFICATE_FOOTER,
cert->raw.p,
cert->raw.len,
buf,
max_pem_cert_size,
&len);
if (rc != 0)
{
throw std::logic_error(
"mbedtls_pem_write_buffer failed: " + error_string(rc));
}
return Pem(buf, len);
}
class CertificateChain
{
public:
mbedtls_x509_crt raw;
CertificateChain()
{
mbedtls_x509_crt_init(&raw);
}
void add(const std::vector<const Pem*>& certs)
{
for (auto& cert : certs)
{
int rc = mbedtls_x509_crt_parse(&raw, cert->data(), cert->size());
if (rc != 0)
{
throw std::runtime_error(
"Could not parse PEM certificate: " + error_string(rc));
}
}
}
void add(const uint8_t* der, size_t len)
{
int rc = mbedtls_x509_crt_parse_der(&raw, der, len);
if (rc != 0)
{
throw std::runtime_error(
"Could not parse DER certificate: " + error_string(rc));
}
}
~CertificateChain()
{
mbedtls_x509_crt_free(&raw);
}
};
bool Verifier_mbedTLS::verify_certificate(
const std::vector<const Pem*>& trusted_certs,
const std::vector<const Pem*>& chain)
{
CertificateChain trusted;
trusted.add(trusted_certs);
mbedtls_x509_crt* crt;
CertificateChain target_and_chain;
if (chain.empty())
{
// Fast-path, avoids extra parse step.
crt = cert.get();
}
else
{
target_and_chain.add(cert.get()->raw.p, cert.get()->raw.len);
target_and_chain.add(chain);
crt = &target_and_chain.raw;
}
uint32_t flags;
int rc = mbedtls_x509_crt_verify(
crt, &trusted.raw, NULL, NULL, &flags, NULL, NULL);
return rc == 0 && flags == 0;
}
bool Verifier_mbedTLS::is_self_signed() const
{
return (cert->issuer_raw.len == cert->subject_raw.len) &&
memcmp(cert->issuer_raw.p, cert->subject_raw.p, cert->subject_raw.len) ==
0;
}
std::string Verifier_mbedTLS::serial_number() const
{
char buf[64];
int rc = mbedtls_x509_serial_gets(buf, sizeof(buf), &cert->serial);
if (rc < 0)
{
throw std::runtime_error(
"mbedtls_x509_serial_gets failed: " + error_string(rc));
}
return buf;
}
std::pair<std::string, std::string> Verifier_mbedTLS::validity_period() const
{
return std::make_pair(
to_x509_time_string(cert->valid_from),
to_x509_time_string(cert->valid_to));
}
}

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

@ -1,36 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the Apache 2.0 License.
#pragma once
#include "crypto/verifier.h"
#include "mbedtls_wrappers.h"
namespace crypto
{
class Verifier_mbedTLS : public Verifier
{
protected:
mutable mbedtls::X509Crt cert;
MDType get_md_type(mbedtls_md_type_t mdt) const;
public:
Verifier_mbedTLS(const std::vector<uint8_t>& c);
Verifier_mbedTLS(const Verifier_mbedTLS&) = delete;
virtual ~Verifier_mbedTLS() = default;
virtual std::vector<uint8_t> cert_der() override;
virtual Pem cert_pem() override;
virtual bool verify_certificate(
const std::vector<const Pem*>& trusted_certs,
const std::vector<const Pem*>& chain = {}) override;
virtual bool is_self_signed() const override;
virtual std::string serial_number() const override;
virtual std::pair<std::string, std::string> validity_period()
const override;
};
}

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

@ -0,0 +1,53 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the Apache 2.0 License.
#pragma once
#include "crypto/entropy.h"
#include "openssl_wrappers.h"
#include <functional>
#include <memory>
#include <openssl/rand.h>
#include <vector>
namespace crypto
{
class Entropy_OpenSSL : public Entropy
{
private:
static bool gen(uint64_t& v);
public:
Entropy_OpenSSL() {}
std::vector<uint8_t> random(size_t len) override
{
std::vector<uint8_t> data(len);
if (RAND_bytes(data.data(), data.size()) != 1)
throw std::logic_error("Couldn't create random data");
return data;
}
uint64_t random64() override
{
uint64_t rnd;
if (RAND_bytes((unsigned char*)&rnd, sizeof(uint64_t)) != 1)
{
throw std::logic_error("Couldn't create random data");
}
return rnd;
}
void random(unsigned char* data, size_t len) override
{
if (RAND_bytes(data, len) != 1)
{
throw std::logic_error("Couldn't create random data");
}
}
};
}

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

@ -25,23 +25,6 @@ namespace crypto
{
using namespace OpenSSL;
static 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::map<std::string, std::string> parse_name(const std::string& name)
{
std::map<std::string, std::string> result;
@ -100,6 +83,17 @@ namespace crypto
return PublicKey_OpenSSL::public_key_der();
}
std::vector<uint8_t> KeyPair_OpenSSL::private_key_der() const
{
Unique_BIO buf;
OpenSSL::CHECK1(i2d_PrivateKey_bio(buf, key));
BUF_MEM* bptr;
BIO_get_mem_ptr(buf, &bptr);
return {bptr->data, bptr->data + bptr->length};
}
bool KeyPair_OpenSSL::verify(
const std::vector<uint8_t>& contents, const std::vector<uint8_t>& signature)
{
@ -343,4 +337,35 @@ namespace crypto
return result;
}
CurveID KeyPair_OpenSSL::get_curve_id() const
{
return PublicKey_OpenSSL::get_curve_id();
}
std::vector<uint8_t> KeyPair_OpenSSL::public_key_raw() const
{
return PublicKey_OpenSSL::public_key_raw();
}
std::vector<uint8_t> KeyPair_OpenSSL::derive_shared_secret(
const PublicKey& peer_key)
{
crypto::CurveID cid = peer_key.get_curve_id();
int nid = crypto::PublicKey_OpenSSL::get_openssl_group_id(cid);
auto pk = key_from_raw_ec_point(peer_key.public_key_raw(), nid);
std::vector<uint8_t> shared_secret;
size_t shared_secret_length = 0;
Unique_EVP_PKEY_CTX ctx(key);
CHECK1(EVP_PKEY_derive_init(ctx));
CHECK1(EVP_PKEY_derive_set_peer(ctx, pk));
CHECK1(EVP_PKEY_derive(ctx, NULL, &shared_secret_length));
shared_secret.resize(shared_secret_length);
CHECK1(EVP_PKEY_derive(ctx, shared_secret.data(), &shared_secret_length));
EVP_PKEY_free(pk);
return shared_secret;
}
}

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

@ -22,6 +22,7 @@ namespace crypto
virtual Pem private_key_pem() const override;
virtual Pem public_key_pem() const override;
virtual std::vector<uint8_t> public_key_der() const override;
virtual std::vector<uint8_t> private_key_der() const override;
using PublicKey_OpenSSL::verify;
@ -60,5 +61,12 @@ namespace crypto
bool ca = false,
const std::optional<std::string>& valid_from = std::nullopt,
const std::optional<std::string>& valid_to = std::nullopt) const override;
virtual std::vector<uint8_t> derive_shared_secret(
const PublicKey& peer_key) override;
virtual CurveID get_curve_id() const override;
virtual std::vector<uint8_t> public_key_raw() const override;
};
}

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

@ -130,6 +130,10 @@ namespace crypto
Unique_SSL_OBJECT(
BIO_new_mem_buf(pem.data(), -1), [](auto x) { BIO_free(x); })
{}
Unique_BIO(SSL_CTX* ctx) :
Unique_SSL_OBJECT(
BIO_new_ssl_connect(ctx), [](auto x) { BIO_free_all(x); })
{}
};
struct Unique_SSL_CTX : public Unique_SSL_OBJECT<SSL_CTX, nullptr, nullptr>
@ -176,18 +180,22 @@ namespace crypto
: public Unique_SSL_OBJECT<X509_CRL, X509_CRL_new, X509_CRL_free>
{
using Unique_SSL_OBJECT::Unique_SSL_OBJECT;
Unique_X509_CRL(BIO* mem) :
Unique_SSL_OBJECT(
PEM_read_bio_X509_CRL(mem, NULL, NULL, NULL), X509_CRL_free)
{}
};
struct Unique_X509 : public Unique_SSL_OBJECT<X509, X509_new, X509_free>
{
using Unique_SSL_OBJECT::Unique_SSL_OBJECT;
// p == nullptr is OK (e.g. wrong format)
Unique_X509(BIO* mem, bool pem) :
Unique_X509(BIO* mem, bool pem, bool check_null = false) :
Unique_SSL_OBJECT(
pem ? PEM_read_bio_X509(mem, NULL, NULL, NULL) :
d2i_X509_bio(mem, NULL),
X509_free,
/*check_null=*/false)
check_null)
{}
Unique_X509(X509* cert, bool check_null) :
Unique_SSL_OBJECT(cert, X509_free, check_null)
@ -266,5 +274,37 @@ namespace crypto
Unique_SSL_OBJECT(t, ASN1_TIME_free, /*check_null=*/false)
{}
};
struct Unique_BN_CTX
: public Unique_SSL_OBJECT<BN_CTX, BN_CTX_new, BN_CTX_free>
{
using Unique_SSL_OBJECT::Unique_SSL_OBJECT;
};
struct Unique_EC_GROUP
: public Unique_SSL_OBJECT<EC_GROUP, nullptr, nullptr>
{
Unique_EC_GROUP(int nid) :
Unique_SSL_OBJECT(
EC_GROUP_new_by_curve_name(nid), EC_GROUP_free, /*check_null=*/true)
{}
};
struct Unique_EC_POINT
: public Unique_SSL_OBJECT<EC_POINT, nullptr, nullptr>
{
Unique_EC_POINT(EC_GROUP* group) :
Unique_SSL_OBJECT(
EC_POINT_new(group), EC_POINT_free, /*check_null=*/true)
{}
};
struct Unique_EC_KEY : public Unique_SSL_OBJECT<EC_KEY, nullptr, nullptr>
{
Unique_EC_KEY(int nid) :
Unique_SSL_OBJECT(
EC_KEY_new_by_curve_name(nid), EC_KEY_free, /*check_null=*/true)
{}
};
}
}

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

@ -10,7 +10,9 @@
#include <openssl/engine.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/ossl_typ.h>
#include <openssl/pem.h>
#include <openssl/x509.h>
#include <openssl/x509v3.h>
#include <stdexcept>
#include <string>
@ -63,6 +65,29 @@ namespace crypto
return CurveID::NONE;
}
int PublicKey_OpenSSL::get_openssl_group_id() const
{
return EC_GROUP_get_curve_name(
EC_KEY_get0_group(EVP_PKEY_get0_EC_KEY(key)));
}
int PublicKey_OpenSSL::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 NID_undef;
}
bool PublicKey_OpenSSL::verify(
const uint8_t* contents,
size_t contents_size,
@ -134,4 +159,39 @@ namespace crypto
BIO_get_mem_ptr(buf, &bptr);
return {bptr->data, bptr->data + bptr->length};
}
std::vector<uint8_t> PublicKey_OpenSSL::public_key_raw() const
{
Unique_BIO buf;
unsigned char* p = NULL;
size_t n = i2d_PublicKey(key, &p);
std::vector<uint8_t> r;
if (p)
{
r = {p, p + n};
}
free(p);
return r;
}
Unique_PKEY key_from_raw_ec_point(const std::vector<uint8_t>& raw, int nid)
{
// To extract a raw encoding of the EC point, OpenSSL has i2d_PublicKey,
// but the converse in d2i_PublicKey is useless until we switch to 3.0
// (see also https://github.com/openssl/openssl/issues/16989).
// So, instead we reconstruct the key the long way round.
Unique_BN_CTX bn_ctx;
Unique_EC_GROUP group(nid);
Unique_EC_POINT p(group);
CHECK1(EC_POINT_oct2point(group, p, raw.data(), raw.size(), bn_ctx));
Unique_EC_KEY ec_key(nid);
CHECK1(EC_KEY_set_public_key(ec_key, p));
Unique_PKEY pk;
CHECK1(EVP_PKEY_set1_EC_KEY(pk, ec_key));
EVP_PKEY_up_ref(pk);
return pk;
}
}

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

@ -3,6 +3,7 @@
#pragma once
#include "../public_key.h"
#include "openssl_wrappers.h"
#include <openssl/err.h>
#include <openssl/evp.h>
@ -16,7 +17,6 @@ namespace crypto
protected:
EVP_PKEY* key = nullptr;
PublicKey_OpenSSL();
CurveID get_curve_id() const;
public:
PublicKey_OpenSSL(PublicKey_OpenSSL&& key) = default;
@ -45,5 +45,19 @@ namespace crypto
virtual Pem public_key_pem() const override;
virtual std::vector<uint8_t> public_key_der() const override;
virtual std::vector<uint8_t> public_key_raw() const override;
virtual CurveID get_curve_id() const override;
int get_openssl_group_id() const;
static int get_openssl_group_id(CurveID gid);
operator EVP_PKEY*() const
{
return key;
}
};
OpenSSL::Unique_PKEY key_from_raw_ec_point(
const std::vector<uint8_t>& raw, int nid);
}

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

@ -2,7 +2,6 @@
// Licensed under the Apache 2.0 License.
#include "symmetric_key.h"
#include "../mbedtls/symmetric_key.h"
#include "crypto/openssl/openssl_wrappers.h"
#include "crypto/symmetric_key.h"
#include "ds/logger.h"

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

@ -157,4 +157,14 @@ namespace crypto
to_x509_time_string(X509_get0_notBefore(cert)),
to_x509_time_string(X509_get0_notAfter(cert)));
}
std::string Verifier_OpenSSL::subject() const
{
X509_NAME* name = X509_get_subject_name(cert);
Unique_BIO mem;
X509_NAME_print_ex(mem, name, 0, 0);
BUF_MEM* bptr;
BIO_get_mem_ptr(mem, &bptr);
return std::string(bptr->data, bptr->length);
}
}

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

@ -35,5 +35,7 @@ namespace crypto
virtual std::pair<std::string, std::string> validity_period()
const override;
virtual std::string subject() const override;
};
}

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

@ -126,5 +126,15 @@ namespace crypto
* Get the public key in DER format
*/
virtual std::vector<uint8_t> public_key_der() const = 0;
/**
* Get the raw bytes of the public key
*/
virtual std::vector<uint8_t> public_key_raw() const = 0;
/**
* The curve ID
*/
virtual CurveID get_curve_id() const = 0;
};
}

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

@ -1,20 +1,14 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the Apache 2.0 License.
#include "mbedtls/rsa_key_pair.h"
#include "openssl/rsa_key_pair.h"
#include "rsa_key_pair.h"
namespace crypto
{
#ifdef CRYPTO_PROVIDER_IS_MBEDTLS
using RSAPublicKeyImpl = RSAPublicKey_mbedTLS;
using RSAKeyPairImpl = RSAKeyPair_mbedTLS;
#else
using RSAPublicKeyImpl = RSAPublicKey_OpenSSL;
using RSAKeyPairImpl = RSAKeyPair_OpenSSL;
#endif
RSAPublicKeyPtr make_rsa_public_key(const Pem& public_pem)
{

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

@ -1,23 +1,16 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the Apache 2.0 License.
#include "crypto/mbedtls/symmetric_key.h"
#include "crypto/openssl/symmetric_key.h"
#include "crypto/rsa_key_pair.h"
#include "symmetric_key.h"
namespace crypto
{
using namespace mbedtls;
std::unique_ptr<KeyAesGcm> make_key_aes_gcm(CBuffer rawKey)
{
#ifdef CRYPTO_PROVIDER_IS_MBEDTLS
return std::make_unique<KeyAesGcm_mbedTLS>(rawKey);
#else
return std::make_unique<KeyAesGcm_OpenSSL>(rawKey);
#endif
}
std::vector<uint8_t> aes_gcm_encrypt(

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

@ -4,10 +4,6 @@
#include "crypto/hash.h"
#include "crypto/hash_provider.h"
#include "crypto/key_pair.h"
#include "crypto/mbedtls/base64.h"
#include "crypto/mbedtls/hash.h"
#include "crypto/mbedtls/key_pair.h"
#include "crypto/mbedtls/rsa_key_pair.h"
#include "crypto/openssl/base64.h"
#include "crypto/openssl/hash.h"
#include "crypto/openssl/key_pair.h"
@ -118,23 +114,14 @@ const std::vector<int> sizes = {10};
PICOBENCH_SUITE("sign secp384r1");
namespace SIGN_SECP384R1
{
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_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_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);
@ -143,23 +130,14 @@ namespace SIGN_SECP384R1
PICOBENCH_SUITE("sign secp256r1");
namespace SIGN_SECP256R1
{
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 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 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);
@ -168,19 +146,10 @@ namespace SIGN_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,
@ -188,12 +157,6 @@ namespace 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,
@ -205,19 +168,10 @@ namespace 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,
@ -225,12 +179,6 @@ namespace 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,
@ -261,18 +209,12 @@ namespace SIGN_RSA2048
auto sign_rsa_ossl_1byte = benchmark_sign<RSAKeyPair_OpenSSL, 2048, 1>;
PICOBENCH(sign_rsa_ossl_1byte).PICO_SUFFIX();
auto sign_rsa_mbed_1byte = benchmark_sign<RSAKeyPair_mbedTLS, 2048, 1>;
PICOBENCH(sign_rsa_mbed_1byte).PICO_SUFFIX();
auto sign_rsa_ossl_1k = benchmark_sign<RSAKeyPair_OpenSSL, 2048, 1024>;
PICOBENCH(sign_rsa_ossl_1k).PICO_SUFFIX();
auto sign_rsa_mbed_1k = benchmark_sign<RSAKeyPair_mbedTLS, 2048, 1024>;
PICOBENCH(sign_rsa_mbed_1k).PICO_SUFFIX();
auto sign_rsa_ossl_100k = benchmark_sign<RSAKeyPair_OpenSSL, 2048, 102400>;
PICOBENCH(sign_rsa_ossl_100k).PICO_SUFFIX();
auto sign_rsa_mbed_100k = benchmark_sign<RSAKeyPair_mbedTLS, 2048, 102400>;
PICOBENCH(sign_rsa_mbed_100k).PICO_SUFFIX();
}
PICOBENCH_SUITE("verify RSA-2048");
@ -306,32 +248,18 @@ namespace VERIFY_RSA2048
auto verify_rsa_ossl_1byte = benchmark_verify<RSAKeyPair_OpenSSL, 2048, 1>;
PICOBENCH(verify_rsa_ossl_1byte).PICO_SUFFIX();
auto verify_rsa_mbed_1byte = benchmark_verify<RSAKeyPair_mbedTLS, 2048, 1>;
PICOBENCH(verify_rsa_mbed_1byte).PICO_SUFFIX();
auto verify_rsa_ossl_1k = benchmark_verify<RSAKeyPair_OpenSSL, 2048, 1024>;
PICOBENCH(verify_rsa_ossl_1k).PICO_SUFFIX();
auto verify_rsa_mbed_1k = benchmark_verify<RSAKeyPair_mbedTLS, 2048, 1024>;
PICOBENCH(verify_rsa_mbed_1k).PICO_SUFFIX();
auto verify_rsa_ossl_100k =
benchmark_verify<RSAKeyPair_OpenSSL, 2048, 102400>;
PICOBENCH(verify_rsa_ossl_100k).PICO_SUFFIX();
auto verify_rsa_mbed_100k =
benchmark_verify<RSAKeyPair_mbedTLS, 2048, 102400>;
PICOBENCH(verify_rsa_mbed_100k).PICO_SUFFIX();
}
PICOBENCH_SUITE("hash");
namespace Hashes
{
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 sha_384_ossl_1byte =
benchmark_hash<OpenSSLHashProvider, MDType::SHA384, 1>;
PICOBENCH(sha_384_ossl_1byte).PICO_HASH_SUFFIX();
@ -342,13 +270,6 @@ namespace Hashes
benchmark_hash<OpenSSLHashProvider, MDType::SHA512, 1>;
PICOBENCH(sha_512_ossl_1byte).PICO_HASH_SUFFIX();
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();
@ -359,16 +280,6 @@ namespace Hashes
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();
@ -380,16 +291,10 @@ namespace Hashes
PICOBENCH(sha_512_ossl_100k).PICO_HASH_SUFFIX();
}
enum Impl
{
mbedtls,
openssl
};
PICOBENCH_SUITE("digest sha256");
namespace SHA256_bench
{
template <Impl IMPL, size_t size>
template <size_t size>
static void sha256_bench(picobench::state& s)
{
std::vector<uint8_t> v(size);
@ -403,48 +308,31 @@ namespace SHA256_bench
s.start_timer();
for (size_t i = 0; i < 10; ++i)
{
if constexpr (IMPL == Impl::mbedtls)
{
crypto::mbedtls_sha256(v, h.h.data());
}
else if constexpr (IMPL == Impl::openssl)
{
crypto::openssl_sha256(v, h.h.data());
}
crypto::openssl_sha256(v, h.h.data());
}
s.stop_timer();
}
auto mbedtls_sha256_base = sha256_bench<Impl::mbedtls, 2 << 6>;
PICOBENCH(mbedtls_sha256_base).PICO_HASH_SUFFIX().baseline();
auto openssl_sha256_base = sha256_bench<Impl::openssl, 2 << 6>;
auto openssl_sha256_base = sha256_bench<2 << 6>;
PICOBENCH(openssl_sha256_base).PICO_HASH_SUFFIX();
auto mbedtls_sha256_8 = sha256_bench<Impl::mbedtls, 2 << 8>;
PICOBENCH(mbedtls_sha256_8).PICO_HASH_SUFFIX();
auto openssl_sha256_8 = sha256_bench<Impl::openssl, 2 << 8>;
auto openssl_sha256_8 = sha256_bench<2 << 8>;
PICOBENCH(openssl_sha256_8).PICO_HASH_SUFFIX();
auto mbedtls_sha256_12 = sha256_bench<Impl::mbedtls, 2 << 12>;
PICOBENCH(mbedtls_sha256_12).PICO_HASH_SUFFIX();
auto openssl_sha256_12 = sha256_bench<Impl::openssl, 2 << 12>;
auto openssl_sha256_12 = sha256_bench<2 << 12>;
PICOBENCH(openssl_sha256_12).PICO_HASH_SUFFIX();
auto mbedtls_sha256_16 = sha256_bench<Impl::mbedtls, 2 << 16>;
PICOBENCH(mbedtls_sha256_16).PICO_HASH_SUFFIX();
auto openssl_sha256_16 = sha256_bench<Impl::openssl, 2 << 16>;
auto openssl_sha256_16 = sha256_bench<2 << 16>;
PICOBENCH(openssl_sha256_16).PICO_HASH_SUFFIX();
auto mbedtls_sha256_18 = sha256_bench<Impl::mbedtls, 2 << 18>;
PICOBENCH(mbedtls_sha256_18).PICO_HASH_SUFFIX();
auto openssl_sha256_18 = sha256_bench<Impl::openssl, 2 << 18>;
auto openssl_sha256_18 = sha256_bench<2 << 18>;
PICOBENCH(openssl_sha256_18).PICO_HASH_SUFFIX();
}
PICOBENCH_SUITE("base64");
namespace Base64_bench
{
template <Impl IMPL, size_t size>
template <size_t size>
static void base64_bench(picobench::state& s)
{
std::vector<uint8_t> v(size);
@ -457,51 +345,30 @@ namespace Base64_bench
for (size_t i = 0; i < 10; ++i)
{
// We don't check the outputs as this is done elsewhere
if constexpr (IMPL == Impl::mbedtls)
{
std::string encoded =
crypto::Base64_mbedtls::b64_from_raw(v.data(), v.size());
crypto::Base64_mbedtls::raw_from_b64(encoded);
}
else if constexpr (IMPL == Impl::openssl)
{
std::string encoded =
crypto::Base64_openssl::b64_from_raw(v.data(), v.size());
crypto::Base64_openssl::raw_from_b64(encoded);
}
std::string encoded =
crypto::Base64_openssl::b64_from_raw(v.data(), v.size());
crypto::Base64_openssl::raw_from_b64(encoded);
}
s.stop_timer();
}
// Single line is 64 chars (48 bytes)
auto mbedtls_base64_base = base64_bench<Impl::mbedtls, 45>;
PICOBENCH(mbedtls_base64_base).PICO_HASH_SUFFIX().baseline();
auto openssl_base64_base = base64_bench<Impl::openssl, 45>;
auto openssl_base64_base = base64_bench<45>;
PICOBENCH(openssl_base64_base).PICO_HASH_SUFFIX();
// Small double line
auto mbedtls_base64_50 = base64_bench<Impl::mbedtls, 50>;
PICOBENCH(mbedtls_base64_50).PICO_HASH_SUFFIX();
auto openssl_base64_50 = base64_bench<Impl::openssl, 50>;
auto openssl_base64_50 = base64_bench<50>;
PICOBENCH(openssl_base64_50).PICO_HASH_SUFFIX();
auto mbedtls_base64_100 = base64_bench<Impl::mbedtls, 100>;
PICOBENCH(mbedtls_base64_100).PICO_HASH_SUFFIX();
auto openssl_base64_100 = base64_bench<Impl::openssl, 100>;
auto openssl_base64_100 = base64_bench<100>;
PICOBENCH(openssl_base64_100).PICO_HASH_SUFFIX();
auto mbedtls_base64_500 = base64_bench<Impl::mbedtls, 500>;
PICOBENCH(mbedtls_base64_500).PICO_HASH_SUFFIX();
auto openssl_base64_500 = base64_bench<Impl::openssl, 500>;
auto openssl_base64_500 = base64_bench<500>;
PICOBENCH(openssl_base64_500).PICO_HASH_SUFFIX();
auto mbedtls_base64_1000 = base64_bench<Impl::mbedtls, 1000>;
PICOBENCH(mbedtls_base64_1000).PICO_HASH_SUFFIX();
auto openssl_base64_1000 = base64_bench<Impl::openssl, 1000>;
auto openssl_base64_1000 = base64_bench<1000>;
PICOBENCH(openssl_base64_1000).PICO_HASH_SUFFIX();
auto mbedtls_base64_5000 = base64_bench<Impl::mbedtls, 5000>;
PICOBENCH(mbedtls_base64_5000).PICO_HASH_SUFFIX();
auto openssl_base64_5000 = base64_bench<Impl::openssl, 5000>;
auto openssl_base64_5000 = base64_bench<5000>;
PICOBENCH(openssl_base64_5000).PICO_HASH_SUFFIX();
}

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

@ -6,12 +6,6 @@
#include "crypto/entropy.h"
#include "crypto/key_pair.h"
#include "crypto/key_wrap.h"
#include "crypto/mbedtls/entropy.h"
#include "crypto/mbedtls/key_pair.h"
#include "crypto/mbedtls/public_key.h"
#include "crypto/mbedtls/rsa_key_pair.h"
#include "crypto/mbedtls/symmetric_key.h"
#include "crypto/mbedtls/verifier.h"
#include "crypto/openssl/key_pair.h"
#include "crypto/openssl/rsa_key_pair.h"
#include "crypto/openssl/symmetric_key.h"
@ -172,16 +166,6 @@ void run_alt()
CHECK(kp2.verify(contents, signature));
}
TEST_CASE("Sign, verify with alternate implementation")
{
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")
{
for (const auto curve : supported_curves)
@ -342,38 +326,6 @@ TEST_CASE("Wrap, unwrap with RSAKeyPair")
}
}
TEST_CASE("RSA-OAEP mbedTLS vs OpenSSL")
{
std::vector<uint8_t> input = create_entropy()->random(32);
size_t key_sz = 2048;
auto kp_mbed = std::make_shared<RSAKeyPair_mbedTLS>(key_sz);
auto pk_mbed =
std::make_shared<RSAPublicKey_mbedTLS>(kp_mbed->public_key_pem());
auto kp_ossl =
std::make_shared<RSAKeyPair_OpenSSL>(kp_mbed->private_key_pem());
auto pk_ossl =
std::make_shared<RSAPublicKey_OpenSSL>(pk_mbed->public_key_pem());
INFO("mbedTLS -> OpenSSL");
{
auto wrapped = pk_mbed->rsa_oaep_wrap(input);
REQUIRE(wrapped.size() == key_sz / 8);
auto unwrapped = kp_ossl->rsa_oaep_unwrap(wrapped);
REQUIRE(input == unwrapped);
}
INFO("OpenSSL -> mbedTLS");
{
auto wrapped = pk_ossl->rsa_oaep_wrap(input);
REQUIRE(wrapped.size() == key_sz / 8);
auto unwrapped = kp_mbed->rsa_oaep_unwrap(wrapped);
REQUIRE(input == unwrapped);
}
}
TEST_CASE("Extract public key from cert")
{
for (const auto curve : supported_curves)
@ -400,7 +352,6 @@ void create_csr_and_extract_pubk()
TEST_CASE("Extract public key from csr")
{
create_csr_and_extract_pubk<KeyPair_mbedTLS>();
create_csr_and_extract_pubk<KeyPair_OpenSSL>();
}
@ -455,9 +406,6 @@ TEST_CASE("Create sign and verify certificates")
bool corrupt_csr = false;
do
{
run_csr<KeyPair_mbedTLS, Verifier_mbedTLS>(corrupt_csr);
run_csr<KeyPair_mbedTLS, Verifier_OpenSSL>(corrupt_csr);
run_csr<KeyPair_OpenSSL, Verifier_mbedTLS>(corrupt_csr);
run_csr<KeyPair_OpenSSL, Verifier_OpenSSL>(corrupt_csr);
corrupt_csr = !corrupt_csr;
} while (corrupt_csr);
@ -484,101 +432,6 @@ TEST_CASE("ExtendedIv0")
REQUIRE(k2->decrypt(h.get_iv(), h.tag, p, nullb, p.p));
}
TEST_CASE("AES mbedTLS vs OpenSSL")
{
auto key = getRawKey();
GcmHeader<1234> h;
{ // mbedTLS -> OpenSSL
auto mbed = std::make_unique<KeyAesGcm_mbedTLS>(key);
auto ossl = std::make_unique<KeyAesGcm_OpenSSL>(key);
std::vector<uint8_t> encrypted(contents_.size());
mbed->encrypt(h.get_iv(), contents_, nullb, encrypted.data(), h.tag);
std::vector<unsigned char> rawP(contents_.size(), 'x');
Buffer p{rawP.data(), rawP.size()};
std::vector<uint8_t> decrypted(contents_.size());
REQUIRE(ossl->decrypt(
h.get_iv(),
h.tag,
{encrypted.data(), encrypted.size()},
nullb,
decrypted.data()));
REQUIRE(decrypted == contents);
}
{ // OpenSSL -> mbedTLS
auto mbed = std::make_unique<KeyAesGcm_mbedTLS>(key);
auto ossl = std::make_unique<KeyAesGcm_OpenSSL>(key);
std::vector<uint8_t> encrypted(contents_.size());
ossl->encrypt(h.get_iv(), contents_, nullb, encrypted.data(), h.tag);
std::vector<unsigned char> rawP(contents_.size(), 'x');
Buffer p{rawP.data(), rawP.size()};
std::vector<uint8_t> decrypted(contents_.size());
CBuffer encbuf{encrypted.data(), encrypted.size()};
REQUIRE(mbed->decrypt(h.get_iv(), h.tag, encbuf, nullb, decrypted.data()));
REQUIRE(decrypted == contents);
}
}
TEST_CASE("AES mbedTLS vs OpenSSL + AAD")
{
auto key = getRawKey();
GcmHeader<1234> h;
std::vector<uint8_t> aad(123, 'y');
{
INFO("mbedTLS -> OpenSSL");
auto mbed = std::make_unique<KeyAesGcm_mbedTLS>(key);
auto ossl = std::make_unique<KeyAesGcm_OpenSSL>(key);
std::vector<uint8_t> encrypted(contents_.size());
mbed->encrypt(h.get_iv(), contents_, aad, encrypted.data(), h.tag);
std::vector<unsigned char> rawP(contents_.size(), 'x');
Buffer p{rawP.data(), rawP.size()};
std::vector<uint8_t> decrypted(contents_.size());
REQUIRE(ossl->decrypt(
h.get_iv(),
h.tag,
{encrypted.data(), encrypted.size()},
aad,
decrypted.data()));
REQUIRE(decrypted == contents);
}
{
INFO("OpenSSL -> mbedTLS");
auto mbed = std::make_unique<KeyAesGcm_mbedTLS>(key);
auto ossl = std::make_unique<KeyAesGcm_OpenSSL>(key);
std::vector<uint8_t> encrypted(contents_.size());
ossl->encrypt(h.get_iv(), contents_, aad, encrypted.data(), h.tag);
std::vector<unsigned char> rawP(contents_.size(), 'x');
Buffer p{rawP.data(), rawP.size()};
std::vector<uint8_t> decrypted(contents_.size());
CBuffer encbuf{encrypted.data(), encrypted.size()};
REQUIRE(mbed->decrypt(h.get_iv(), h.tag, encbuf, aad, decrypted.data()));
REQUIRE(decrypted == contents);
}
}
TEST_CASE("AES Key wrap with padding")
{
auto key = getRawKey();
@ -729,26 +582,3 @@ TEST_CASE("x509 time")
}
}
}
TEST_CASE("check equality of DER encodings")
{
{
KeyPair_OpenSSL key_ossl(CurveID::SECP384R1);
auto der_ossl = key_ossl.public_key_der();
PublicKey_mbedTLS pub_mbed(der_ossl);
auto der_mbed = pub_mbed.public_key_der();
REQUIRE(der_ossl == der_mbed);
PublicKey_OpenSSL pub_ossl(pub_mbed.public_key_der());
REQUIRE(der_mbed == der_ossl);
}
{
KeyPair_mbedTLS key_mbed(CurveID::SECP384R1);
auto der_mbed = key_mbed.public_key_der();
PublicKey_OpenSSL pub_ossl(der_mbed);
auto der_ossl = pub_ossl.public_key_der();
REQUIRE(der_ossl == der_mbed);
PublicKey_mbedTLS pub_mbed(pub_ossl.public_key_der());
REQUIRE(der_mbed == der_ossl);
}
}

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

@ -4,6 +4,8 @@
#include "crypto/key_exchange.h"
#include "crypto/openssl/key_pair.h"
#include <doctest/doctest.h>
TEST_CASE("Simple key exchange")
@ -54,9 +56,9 @@ TEST_CASE("Simple key exchange")
TEST_CASE("Key exchange from static shares")
{
auto peer1_kp = std::make_shared<crypto::KeyPair_mbedTLS>(
auto peer1_kp = std::make_shared<crypto::KeyPair_OpenSSL>(
crypto::service_identity_curve_choice);
auto peer2_kp = std::make_shared<crypto::KeyPair_mbedTLS>(
auto peer2_kp = std::make_shared<crypto::KeyPair_OpenSSL>(
crypto::service_identity_curve_choice);
auto peer1_ctx = tls::KeyExchangeContext(peer1_kp, peer2_kp);

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

@ -1,10 +1,9 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the Apache 2.0 License.
#include "crypto/mbedtls/verifier.h"
#include "verifier.h"
#include "crypto/openssl/verifier.h"
#include "verifier.h"
namespace crypto
{
@ -13,20 +12,12 @@ namespace crypto
VerifierUniquePtr make_unique_verifier(const std::vector<uint8_t>& cert)
{
#ifdef CRYPTO_PROVIDER_IS_MBEDTLS
return std::make_unique<Verifier_mbedTLS>(cert);
#else
return std::make_unique<Verifier_OpenSSL>(cert);
#endif
}
VerifierPtr make_verifier(const std::vector<uint8_t>& cert)
{
#ifdef CRYPTO_PROVIDER_IS_MBEDTLS
return std::make_shared<Verifier_mbedTLS>(cert);
#else
return std::make_shared<Verifier_OpenSSL>(cert);
#endif
}
VerifierUniquePtr make_unique_verifier(const Pem& pem)

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

@ -197,6 +197,9 @@ namespace crypto
/** The validity period of the certificate */
virtual std::pair<std::string, std::string> validity_period() const = 0;
/** The subject name of the certificate */
virtual std::string subject() const = 0;
};
using VerifierPtr = std::shared_ptr<Verifier>;

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

@ -191,10 +191,8 @@ namespace enclave
// Caller authentication is done by each frontend by looking up
// the caller's certificate in the relevant store table. The caller
// certificate does not have to be signed by a known CA (nullptr,
// tls::auth_optional).
cert = std::make_shared<tls::Cert>(
nullptr, cert_, pk, nullb, tls::auth_optional);
// certificate does not have to be signed by a known CA (nullptr).
cert = std::make_shared<tls::Cert>(nullptr, cert_, pk, tls::auth_default);
}
void accept(tls::ConnID id, const ListenInterfaceID& listen_interface_id)

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

@ -9,9 +9,7 @@
#include "enclave/endpoint.h"
#include "tls/context.h"
#include "tls/msg_types.h"
// These headers are temporary, until we have a single TLS implementation
#include "tls/mbedtls/tls.h"
#include "tls/openssl/tls.h"
#include "tls/tls.h"
#include <exception>
@ -71,13 +69,7 @@ namespace enclave
{
execution_thread =
threading::ThreadMessaging::get_execution_thread(session_id);
// The functions we pass are different so this can only be done
// with an ifdef. Remove once we get rid of mbedTLS.
#ifdef TLS_PROVIDER_IS_MBEDTLS
ctx->set_bio(this, send_callback, recv_callback, dbg_callback);
#else
ctx->set_bio(this, send_callback_openssl, recv_callback_openssl, nullptr);
#endif
}
~TLSEndpoint()
@ -565,7 +557,6 @@ namespace enclave
// emulate MbedTLS.
// Once we get rid of MbedTLS we can move the callbacks above to handle BIOs
// directly and hopefully remove the complexity below.
#ifndef TLS_PROVIDER_IS_MBEDTLS
static long send_callback_openssl(
BIO* b,
int oper,
@ -680,8 +671,6 @@ namespace enclave
return ret;
}
#endif
// Remove this function once MbedTLS is gone.
static void dbg_callback(
void*, int, const char* file, int line, const char* str)

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

@ -18,7 +18,6 @@
#include <iostream>
#include <map>
#include <mbedtls/ecdh.h>
#include <openssl/crypto.h>
// -Wpedantic flags token pasting of __VA_ARGS__
@ -701,7 +700,6 @@ namespace ccf
void establish()
{
auto shared_secret = kex_ctx.compute_shared_secret();
kex_ctx.free_ctx();
{
const std::string label_from = peer_id.value() + self.value();

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

@ -7,7 +7,6 @@
#include "entities.h"
#include "service_map.h"
#include <mbedtls/md.h>
#include <vector>
namespace ccf
@ -41,4 +40,4 @@ namespace ccf
};
DECLARE_JSON_TYPE(SignedReq)
DECLARE_JSON_REQUIRED_FIELDS(SignedReq, sig, req, request_body, md, key_id)
}
}

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

@ -20,7 +20,6 @@
#include <string.h>
#define HAVE_OPENSSL
#define HAVE_MBEDTLS
// merklecpp traces are off by default, even when CCF tracing is enabled
// #include "merklecpp_trace.h"
#include <merklecpp/merklecpp.h>

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

@ -243,12 +243,7 @@ namespace ccf
auto jwks_url_port = !jwks_url.port.empty() ? jwks_url.port : "443";
auto ca_cert = std::make_shared<tls::Cert>(
ca,
std::nullopt,
std::nullopt,
nullb,
tls::auth_required,
jwks_url.host);
ca, std::nullopt, std::nullopt, tls::auth_required, jwks_url.host);
LOG_DEBUG_FMT(
"JWT key auto-refresh: Requesting JWKS at https://{}:{}{}",
@ -313,7 +308,6 @@ namespace ccf
ca,
std::nullopt,
std::nullopt,
nullb,
tls::auth_required,
metadata_url.host);

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

@ -113,7 +113,7 @@ TEST_CASE("Check signature verification")
auto txs = primary_store.create_tx();
auto sigs = txs.rw(signatures);
ccf::PrimarySignature bogus(kv::test::PrimaryNodeId, 0);
bogus.sig = std::vector<uint8_t>(MBEDTLS_ECDSA_MAX_LEN, 1);
bogus.sig = std::vector<uint8_t>(256, 1);
sigs->put(bogus);
REQUIRE(txs.commit() == kv::CommitResult::FAIL_NO_REPLICATE);
}

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

@ -1,6 +1,6 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the Apache 2.0 License.
#include "crypto/mbedtls/hash.h"
#include "crypto/hash.h"
#include "kv/test/stub_consensus.h"
#include "node/history.h"
@ -59,7 +59,7 @@ static void hash_only(picobench::state& s)
(void)_;
auto data = txs[idx++];
crypto::Sha256Hash h;
crypto::mbedtls_sha256({data}, h.h.data());
crypto::default_sha256({data}, h.h.data());
do_not_optimize(h);
clobber_memory();
}
@ -166,4 +166,4 @@ int main(int argc, char* argv[])
picobench::runner runner;
runner.parse_cmd_line(argc, argv);
return runner.run();
}
}

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

@ -204,7 +204,7 @@ namespace client
private:
crypto::Pem key = {};
std::string key_id = "Invalid";
std::shared_ptr<tls::TlsCert> tls_cert = nullptr;
std::shared_ptr<tls::Cert> tls_cert = nullptr;
// Process reply to an RPC. Records time reply was received. Calls
// check_response for derived-overridable validation
@ -305,8 +305,8 @@ namespace client
auto cert_der = crypto::cert_pem_to_der(raw_cert);
key_id = crypto::Sha256Hash(cert_der).hex_str();
tls_cert = std::make_shared<tls::TlsCert>(
std::make_shared<tls::TlsCA>(ca), raw_cert, key);
tls_cert = std::make_shared<tls::Cert>(
std::make_shared<tls::CA>(ca), raw_cert, key);
}
const auto [host, port] = ccf::split_net_address(options.server_address);

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

@ -15,7 +15,6 @@
#define FMT_HEADER_ONLY
#include <fmt/format.h>
#include <mbedtls/asn1.h>
namespace timing
{

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

@ -1,4 +1,70 @@
# TLS
# OpenSSL TLS Implementation
This directory implements a set of wrappers around mbedtls. The only directories
it should depend on is `ds` and `crypto`.
This is a TLS implementation using OpenSSL that mimics the existing MbedTLS
one in a similar fashion. Because of that, some structures and call backs
look odd and have some work-arounds to make it fit the current workflow.
Once we completely deprecate the MbedTLS implementation from CCF, we should
re-write the TLS implementation to fit the OpenSSL coding flow, which would
make it much simpler and easier to use.
## CAs and Certificates
In the MbedTLS world, certificates can be null and have methods to change
some configurations in the TLS config/session objects. There isn't a lot of
cross-over, so updating the config does the trick.
However, in OpenSSL, session objects (ssl) are created from config objects
(cfg) and inherit all its properties. Therefore, to emulate MbedTLS, we need
to do to the session object every action we do to the config object, which is
not only redundant, but could be unsafe, if the calls are slightly different.
### Validation
Certificate validation can be complex to handle if you can accept connections
with certificates or not, and if they come, when and how to validate.
MbedTLS is a lot more lenient on checks. For example, CAs are not tested for
validity of actually signing other certificates, while OpenSSL has extensive
checks, which can fail functionality that was previously passing.
For this reason, a number of extra checks in the OpenSSL side were disabled.
Once we get rid of MbedTLS we should revisit those checks again and improve
CCF's usage of TLS, and perhaps also creating weaker checks for non-CA
certificates, etc.
## Context
### BIOs
MbedTLS operates reads and writes solely via callbacks, with a buffer in the
session object acting as async I/O. This is in stark contrast with OpenSSL
which uses BIO objects to pass information back and forth, and only have
callbacks for debug or very specialized cases.
We had to implement callbacks and specialize our case, but it could really be
just done with BIOs between the ring buffer and the context, but we'd have to
change a lot of code outside of the TLS implementation to add that.
### Reads and Writes
Reading and writing in MbedTLS returns a positive value for success (number of
bytes written) or a negative value for error (pre-defined error codes) including
WANTS_READ and WANTS_WRITE.
In OpenSSL, those methods return 1 for success and 0 or -1 for errors (depending
on the version), with all errors, including WANTS_READ and WANTS_WRITE
accessible through `SSL_get_error`. This imposes a number of hacks needed to
mimic the MbedTLS implementation, including:
- Multiple `#define`s with common error messages in `tls.h`
- Having to negate the error code to match
- Multiple checks to `SSL_want_read` and `SSL_want_write`
### Error Handling
As discussed above, the error handling is slightly different and promotes
verbose code in OpenSSL's side.
Another example is `verify_result` and `get_verify_error` that is very long
in the OpenSSL implementation and two one-liners in MbedTLS.

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

@ -2,8 +2,50 @@
// Licensed under the Apache 2.0 License.
#pragma once
#ifdef TLS_PROVIDER_IS_MBEDTLS
# include "mbedtls/ca.h"
#else
# include "openssl/ca.h"
#endif
#include "crypto/openssl/openssl_wrappers.h"
#include "crypto/pem.h"
#include "ds/buffer.h"
#include <exception>
using namespace crypto;
using namespace crypto::OpenSSL;
namespace tls
{
enum Auth
{
auth_default,
auth_none,
auth_required
};
class CA
{
private:
Unique_X509 ca;
public:
CA(CBuffer ca_ = nullb)
{
if (ca_.n > 0)
{
Unique_BIO bio(ca_.p, ca_.n);
if (!(ca = Unique_X509(bio, true)))
{
throw std::logic_error(
"Could not parse CA: " + error_string(ERR_get_error()));
}
}
}
~CA() = default;
void use(SSL_CTX* ssl_ctx)
{
X509_STORE* store = X509_STORE_new();
CHECK1(X509_STORE_add_cert(store, ca));
SSL_CTX_set_cert_store(ssl_ctx, store);
}
};
}

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

@ -2,8 +2,125 @@
// Licensed under the Apache 2.0 License.
#pragma once
#ifdef TLS_PROVIDER_IS_MBEDTLS
# include "mbedtls/cert.h"
#else
# include "openssl/cert.h"
#endif
#include "crypto/openssl/key_pair.h"
#include "crypto/openssl/openssl_wrappers.h"
#include "tls/ca.h"
#include <cstring>
#include <memory>
#include <openssl/x509.h>
#include <optional>
using namespace crypto;
using namespace crypto::OpenSSL;
namespace tls
{
// This class represents the authentication/authorization context for a TLS
// session. At least, it contains the peer's CA. At most, it also contains our
// own private key/certificate which will be presented in the TLS handshake.
// The peer's certificate verification can be overridden with the auth
// parameter.
class Cert
{
private:
std::shared_ptr<CA> peer_ca;
std::optional<std::string> peer_hostname;
Auth auth;
Unique_X509 own_cert;
std::shared_ptr<KeyPair_OpenSSL> own_pkey;
bool has_own_cert = false;
public:
Cert(
std::shared_ptr<CA> peer_ca_,
const std::optional<crypto::Pem>& own_cert_ = std::nullopt,
const std::optional<crypto::Pem>& own_pkey_ = std::nullopt,
Auth auth_ = auth_default,
const std::optional<std::string>& peer_hostname_ = std::nullopt) :
peer_ca(peer_ca_),
peer_hostname(peer_hostname_),
auth(auth_)
{
if (own_cert_.has_value() && own_pkey_.has_value())
{
Unique_BIO certbio(*own_cert_);
own_cert = Unique_X509(certbio, true);
own_pkey = std::make_shared<KeyPair_OpenSSL>(*own_pkey_);
has_own_cert = true;
}
}
~Cert() = default;
void use(SSL* ssl, SSL_CTX* ssl_ctx)
{
if (peer_hostname.has_value())
{
// Peer hostname is only checked against peer certificate (SAN
// extension) if it is set. This lets us connect to peers that present
// certificates with IPAddress in SAN field (mbedtls does not parse
// IPAddress in SAN field). This is OK since we check for peer CA
// endorsement.
SSL_set1_host(ssl, peer_hostname->c_str());
}
if (peer_ca)
{
peer_ca->use(ssl_ctx);
}
if (auth != auth_default)
{
SSL_CTX_set_verify(ssl_ctx, authmode(auth), NULL);
}
else
{
// Calling set_verify with SSL_VERIFY_PEER forces the handshake to
// request a peer certificate. The server always sends it to the client
// but not the other way around. Some code relies on the server doing
// that, so we set this here. We return 1 from the validation callback
// (a common pattern in OpenSSL implementations) because we don't want
// to verify it here, just request it.
SSL_CTX_set_verify(
ssl_ctx, SSL_VERIFY_PEER, [](int precheck, x509_store_ctx_st* st) {
(void)precheck;
(void)st;
return 1;
});
SSL_set_verify(
ssl, SSL_VERIFY_PEER, [](int precheck, x509_store_ctx_st* st) {
(void)precheck;
(void)st;
return 1;
});
}
if (has_own_cert)
{
CHECK1(SSL_CTX_use_cert_and_key(ssl_ctx, own_cert, *own_pkey, NULL, 1));
CHECK1(SSL_use_cert_and_key(ssl, own_cert, *own_pkey, NULL, 1));
}
}
private:
int authmode(Auth auth)
{
switch (auth)
{
case auth_none:
{
// Peer certificate is not checked
return SSL_VERIFY_NONE;
}
case auth_required:
default:
{
return SSL_VERIFY_PEER;
}
}
}
};
}

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

@ -2,8 +2,21 @@
// Licensed under the Apache 2.0 License.
#pragma once
#ifdef TLS_PROVIDER_IS_MBEDTLS
# include "mbedtls/client.h"
#else
# include "openssl/client.h"
#endif
#include "context.h"
namespace tls
{
class Client : public Context
{
private:
std::shared_ptr<Cert> cert;
public:
Client(std::shared_ptr<Cert> cert_, bool dtls = false) :
Context(true, dtls),
cert(cert_)
{
cert->use(ssl, cfg);
}
};
}

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

@ -2,8 +2,518 @@
// Licensed under the Apache 2.0 License.
#pragma once
#ifdef TLS_PROVIDER_IS_MBEDTLS
# include "mbedtls/context.h"
#else
# include "openssl/context.h"
#endif
#include "cert.h"
#include "crypto/base64.h"
#include "crypto/entropy.h"
#include "ds/logger.h"
#include "tls/tls.h"
#include <memory>
#include <openssl/bio.h>
#include <openssl/ssl.h>
using namespace crypto;
namespace tls
{
class Context
{
protected:
crypto::OpenSSL::Unique_SSL_CTX cfg;
crypto::OpenSSL::Unique_SSL ssl;
public:
Context(bool client, bool dtls) :
cfg(
dtls ? (client ? DTLS_client_method() : DTLS_server_method()) :
(client ? TLS_client_method() : TLS_server_method())),
ssl(cfg)
{
// Require at least TLS 1.2, support up to 1.3
SSL_CTX_set_min_proto_version(
cfg, dtls ? DTLS1_2_VERSION : TLS1_2_VERSION);
SSL_set_min_proto_version(ssl, dtls ? DTLS1_2_VERSION : TLS1_2_VERSION);
// Disable renegotiation to avoid DoS
SSL_CTX_set_options(
cfg,
SSL_OP_CIPHER_SERVER_PREFERENCE |
SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION |
SSL_OP_NO_RENEGOTIATION);
SSL_set_options(
ssl,
SSL_OP_CIPHER_SERVER_PREFERENCE |
SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION |
SSL_OP_NO_RENEGOTIATION);
// Set cipher for TLS 1.2 (TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256)
SSL_CTX_set_cipher_list(
cfg,
"ECDHE-ECDSA-AES256-GCM-SHA384:"
"ECDHE-ECDSA-AES128-GCM-SHA256");
SSL_set_cipher_list(
ssl,
"ECDHE-ECDSA-AES256-GCM-SHA384:"
"ECDHE-ECDSA-AES128-GCM-SHA256");
// Set cipher for TLS 1.3 (same as above)
SSL_CTX_set_ciphersuites(
cfg,
"TLS_AES_256_GCM_SHA384:"
"TLS_AES_128_GCM_SHA256");
SSL_set_ciphersuites(
ssl,
"TLS_AES_256_GCM_SHA384:"
"TLS_AES_128_GCM_SHA256");
// Restrict the curves to approved ones
SSL_CTX_set1_curves_list(cfg, "P-521:P-384");
SSL_set1_curves_list(ssl, "P-521:P-384");
// Initialise connection
if (client)
SSL_set_connect_state(ssl);
else
SSL_set_accept_state(ssl);
}
virtual ~Context() = default;
void set_bio(
void* cb_obj,
BIO_callback_fn_ex send,
BIO_callback_fn_ex recv,
BIO_callback_fn_ex dbg)
{
// Read/Write BIOs will be used by TLS
BIO* rbio = BIO_new(BIO_s_mem());
BIO_set_mem_eof_return(rbio, -1);
BIO_set_callback_arg(rbio, (char*)cb_obj);
BIO_set_callback_ex(rbio, recv);
SSL_set0_rbio(ssl, rbio);
BIO* wbio = BIO_new(BIO_s_mem());
BIO_set_mem_eof_return(wbio, -1);
BIO_set_callback_arg(wbio, (char*)cb_obj);
BIO_set_callback_ex(wbio, send);
SSL_set0_wbio(ssl, wbio);
}
int handshake()
{
if (SSL_is_init_finished(ssl))
return 0;
int rc = SSL_do_handshake(ssl);
// Success in OpenSSL is 1, MBed is 0
if (rc > 0)
{
LOG_TRACE_FMT("Context::handshake() : Success");
return 0;
}
// Want read/write needs special return
if (SSL_want_read(ssl))
{
return TLS_ERR_WANT_READ;
}
else if (SSL_want_write(ssl))
{
return TLS_ERR_WANT_WRITE;
}
// So does x509 validation
if (verify_result() != 0)
{
return TLS_ERR_X509_VERIFY;
}
// Everything else falls here.
LOG_TRACE_FMT("Context::handshake() : Error code {}", rc);
// As an MBedTLS emulation, we return negative for errors.
return -SSL_get_error(ssl, rc);
}
int read(uint8_t* buf, size_t len)
{
if (len == 0)
return 0;
size_t readbytes = 0;
int rc = SSL_read_ex(ssl, buf, len, &readbytes);
if (rc > 0)
{
return readbytes;
}
if (SSL_want_read(ssl))
{
return TLS_ERR_WANT_READ;
}
// Everything else falls here.
LOG_TRACE_FMT("Context::read() : Error code {}", rc);
// As an MBedTLS emulation, we return negative for errors.
return -SSL_get_error(ssl, rc);
}
int write(const uint8_t* buf, size_t len)
{
if (len == 0)
return 0;
size_t written = 0;
int rc = SSL_write_ex(ssl, buf, len, &written);
if (rc > 0)
{
return written;
}
if (SSL_want_write(ssl))
{
return TLS_ERR_WANT_WRITE;
}
// Everything else falls here.
LOG_TRACE_FMT("Context::write() : Error code {}", rc);
// As an MBedTLS emulation, we return negative for errors.
return -SSL_get_error(ssl, rc);
}
int close()
{
LOG_TRACE_FMT("Context::close() : Shutdown");
return SSL_shutdown(ssl);
}
// This is a hack to make it work like MBedTLS (with negative return
// values as error), and to differentiate in get_verify_error if the error
// is because we don't have a peer cert or something else.
// We may find that this is unnecessary (alongside all of the error messages
// in get_verify_error), but that's a cleanup that will need a bit more
// research.
#define TLS_ERR_X509_NO_PEER_CERT -1
#define TLS_ERR_X509_INVALID_RESULT -2
int verify_result()
{
if (SSL_get_verify_result(ssl) == X509_V_OK)
{
// Verify can return OK when no certificate is presented
// We want that to be an error
X509* cert = SSL_get_peer_certificate(ssl);
if (cert)
{
X509_free(cert);
return 0;
}
else
{
return TLS_ERR_X509_NO_PEER_CERT;
}
}
return TLS_ERR_X509_INVALID_RESULT;
}
std::string get_verify_error()
{
int rc = verify_result();
if (rc == TLS_ERR_X509_NO_PEER_CERT)
{
return "Certificate verify error: No peer certificate";
}
switch (rc)
{
case X509_V_ERR_UNSPECIFIED:
return "Unspecified error; should not happen.";
case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
return "The issuer certificate of a looked up certificate could not "
"be found. This normally means the list of trusted "
"certificates is not complete.";
case X509_V_ERR_UNABLE_TO_GET_CRL:
return "The CRL of a certificate could not be found.";
case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE:
return "The certificate signature could not be decrypted. This means "
"that the actual signature value could not be determined "
"rather than it not matching the expected value";
case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE:
return "The CRL signature could not be decrypted: this means that "
"the actual signature value could not be determined rather "
"than it not matching the expected value. Unused.";
case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY:
return "The public key in the certificate SubjectPublicKeyInfo could "
"not be read.";
case X509_V_ERR_CERT_SIGNATURE_FAILURE:
return "The signature of the certificate is invalid.";
case X509_V_ERR_CRL_SIGNATURE_FAILURE:
return "The signature of the certificate is invalid.";
case X509_V_ERR_CERT_NOT_YET_VALID:
return "The certificate is not yet valid: the notBefore date is "
"after the current time.";
case X509_V_ERR_CERT_HAS_EXPIRED:
return "The certificate has expired: that is the notAfter date is "
"before the current time.";
case X509_V_ERR_CRL_NOT_YET_VALID:
return "The CRL is not yet valid.";
case X509_V_ERR_CRL_HAS_EXPIRED:
return "The CRL has expired.";
case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
return "The certificate notBefore field contains an invalid time.";
case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
return "The certificate notAfter field contains an invalid time.";
case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD:
return "The CRL lastUpdate field contains an invalid time.";
case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD:
return "The CRL nextUpdate field contains an invalid time.";
case X509_V_ERR_OUT_OF_MEM:
return "An error occurred trying to allocate memory. This should "
"never happen.";
case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
return "The passed certificate is self-signed and the same "
"certificate cannot be found in the list of trusted "
"certificates.";
case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
return "The certificate chain could be built up using the untrusted "
"certificates but the root could not be found locally.";
case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
return "The issuer certificate could not be found: this occurs if "
"the issuer certificate of an untrusted certificate cannot be "
"found.";
case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
return "No signatures could be verified because the chain contains "
"only one certificate and it is not self signed.";
case X509_V_ERR_CERT_CHAIN_TOO_LONG:
return "The certificate chain length is greater than the supplied "
"maximum depth. Unused.";
case X509_V_ERR_CERT_REVOKED:
return "The certificate has been revoked.";
case X509_V_ERR_INVALID_CA:
return "A CA certificate is invalid. Either it is not a CA or its "
"extensions are not consistent with the supplied purpose.";
case X509_V_ERR_PATH_LENGTH_EXCEEDED:
return "The basicConstraints pathlength parameter has been exceeded.";
case X509_V_ERR_INVALID_PURPOSE:
return "The supplied certificate cannot be used for the specified "
"purpose.";
case X509_V_ERR_CERT_UNTRUSTED:
return "The root CA is not marked as trusted for the specified "
"purpose.";
case X509_V_ERR_CERT_REJECTED:
return "The root CA is marked to reject the specified purpose.";
case X509_V_ERR_SUBJECT_ISSUER_MISMATCH:
return "Not used as of OpenSSL 1.1.0 as a result of the deprecation "
"of the -issuer_checks option.";
case X509_V_ERR_AKID_SKID_MISMATCH:
return "Not used as of OpenSSL 1.1.0 as a result of the deprecation "
"of the -issuer_checks option.";
case X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH:
return "Not used as of OpenSSL 1.1.0 as a result of the deprecation "
"of the -issuer_checks option.";
case X509_V_ERR_KEYUSAGE_NO_CERTSIGN:
return "Not used as of OpenSSL 1.1.0 as a result of the deprecation "
"of the -issuer_checks option.";
case X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER:
return "Unable to get CRL issuer certificate.";
case X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION:
return "Unhandled critical extension.";
case X509_V_ERR_KEYUSAGE_NO_CRL_SIGN:
return "Key usage does not include CRL signing.";
case X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION:
return "Unhandled critical CRL extension.";
case X509_V_ERR_INVALID_NON_CA:
return "Invalid non-CA certificate has CA markings.";
case X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED:
return "Proxy path length constraint exceeded.";
case X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE:
return "Key usage does not include digital signature.";
case X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED:
return "Proxy certificates not allowed";
case X509_V_ERR_INVALID_EXTENSION:
return "Invalid or inconsistent certificate extension.";
case X509_V_ERR_INVALID_POLICY_EXTENSION:
return "Invalid or inconsistent certificate policy extension.";
case X509_V_ERR_NO_EXPLICIT_POLICY:
return "No explicit policy.";
case X509_V_ERR_DIFFERENT_CRL_SCOPE:
return "Different CRL scope.";
case X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE:
return "Unsupported extension feature.";
case X509_V_ERR_UNNESTED_RESOURCE:
return "RFC 3779 resource not subset of parent's resources.";
case X509_V_ERR_PERMITTED_VIOLATION:
return "Permitted subtree violation.";
case X509_V_ERR_EXCLUDED_VIOLATION:
return "Excluded subtree violation.";
case X509_V_ERR_SUBTREE_MINMAX:
return "Name constraints minimum and maximum not supported.";
case X509_V_ERR_APPLICATION_VERIFICATION:
return "Application verification failure. Unused.";
case X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE:
return "Unsupported name constraint type.";
case X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX:
return "Unsupported or invalid name constraint syntax.";
case X509_V_ERR_UNSUPPORTED_NAME_SYNTAX:
return "Unsupported or invalid name syntax.";
case X509_V_ERR_CRL_PATH_VALIDATION_ERROR:
return "CRL path validation error.";
case X509_V_ERR_PATH_LOOP:
return "Path loop.";
case X509_V_ERR_SUITE_B_INVALID_VERSION:
return "Suite B: certificate version invalid.";
case X509_V_ERR_SUITE_B_INVALID_ALGORITHM:
return "Suite B: invalid public key algorithm.";
case X509_V_ERR_SUITE_B_INVALID_CURVE:
return "Suite B: invalid ECC curve.";
case X509_V_ERR_SUITE_B_INVALID_SIGNATURE_ALGORITHM:
return "Suite B: invalid signature algorithm.";
case X509_V_ERR_SUITE_B_LOS_NOT_ALLOWED:
return "Suite B: curve not allowed for this LOS.";
case X509_V_ERR_SUITE_B_CANNOT_SIGN_P_384_WITH_P_256:
return "Suite B: cannot sign P-384 with P-256.";
case X509_V_ERR_HOSTNAME_MISMATCH:
return "Hostname mismatch.";
case X509_V_ERR_EMAIL_MISMATCH:
return "Email address mismatch.";
case X509_V_ERR_IP_ADDRESS_MISMATCH:
return "IP address mismatch.";
case X509_V_ERR_DANE_NO_MATCH:
return "DANE TLSA authentication is enabled";
case X509_V_ERR_EE_KEY_TOO_SMALL:
return "EE certificate key too weak.";
case X509_V_ERR_INVALID_CALL:
return "nvalid certificate verification context.";
case X509_V_ERR_STORE_LOOKUP:
return "Issuer certificate lookup error.";
case X509_V_ERR_NO_VALID_SCTS:
return "Certificate Transparency required";
case X509_V_ERR_PROXY_SUBJECT_NAME_VIOLATION:
return "Proxy subject name violation.";
case X509_V_ERR_OCSP_VERIFY_NEEDED:
return "Returned by the verify callback to indicate an OCSP "
"verification is needed.";
case X509_V_ERR_OCSP_VERIFY_FAILED:
return "Returned by the verify callback to indicate OCSP "
"verification failed.";
case X509_V_ERR_OCSP_CERT_UNKNOWN:
return "Returned by the verify callback to indicate that the "
"certificate is not recognized by the OCSP responder.";
}
return "";
}
virtual std::string host()
{
return {};
}
std::vector<uint8_t> peer_cert()
{
// Get the certificate into a BIO as DER
crypto::OpenSSL::Unique_X509 cert(
SSL_get_peer_certificate(ssl), /*check_null=*/false);
if (!cert)
{
LOG_TRACE_FMT("Empty peer cert");
return {};
}
crypto::OpenSSL::Unique_BIO bio;
if (!i2d_X509_bio(bio, cert))
{
LOG_TRACE_FMT("Can't convert X509 to DER");
return {};
}
// Get the total length of the DER representation
auto len = BIO_get_mem_data(bio, nullptr);
if (!len)
{
LOG_TRACE_FMT("Null X509 peer cert");
return {};
}
// Get the BIO memory pointer
BUF_MEM* ptr = nullptr;
if (!BIO_get_mem_ptr(bio, &ptr))
{
LOG_TRACE_FMT("Invalid X509 peer cert");
return {};
}
// Return its contents as a vector
auto ret = std::vector<uint8_t>(ptr->data, ptr->data + len);
return ret;
}
};
}

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

@ -1,57 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the Apache 2.0 License.
#pragma once
#include "crypto/mbedtls/error_string.h"
#include "crypto/mbedtls/mbedtls_wrappers.h"
#include "crypto/pem.h"
#include "ds/buffer.h"
#include <exception>
namespace tls
{
class CA
{
private:
crypto::mbedtls::X509Crt ca = nullptr;
crypto::mbedtls::X509Crl crl = nullptr;
public:
CA(CBuffer ca_ = nullb, CBuffer crl_ = nullb)
{
auto tmp_ca = crypto::mbedtls::make_unique<crypto::mbedtls::X509Crt>();
auto tmp_crl = crypto::mbedtls::make_unique<crypto::mbedtls::X509Crl>();
if (ca_.n > 0)
{
crypto::Pem pem_ca(ca_);
auto ret =
mbedtls_x509_crt_parse(tmp_ca.get(), pem_ca.data(), pem_ca.size());
if (ret != 0)
throw std::logic_error(
"Could not parse CA: " + crypto::error_string(ret));
}
if (crl_.n > 0)
{
crypto::Pem pem_crl(crl_);
auto ret =
mbedtls_x509_crl_parse(tmp_crl.get(), pem_crl.data(), pem_crl.size());
if (ret != 0)
throw std::logic_error(
"Could not parse CRL: " + crypto::error_string(ret));
}
ca = std::move(tmp_ca);
crl = std::move(tmp_crl);
}
~CA() {}
void use(mbedtls_ssl_config* cfg)
{
mbedtls_ssl_conf_ca_chain(cfg, ca.get(), crl.get());
}
};
}

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

@ -1,160 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the Apache 2.0 License.
#pragma once
#include "ca.h"
#include "crypto/mbedtls/error_string.h"
#include "crypto/mbedtls/mbedtls_wrappers.h"
#include <cstring>
#include <memory>
#include <optional>
using namespace crypto;
namespace tls
{
enum Auth
{
auth_default,
auth_none,
auth_optional,
auth_required
};
// This class represents the authentication/authorization context for a TLS
// session. At least, it contains the peer's CA. At most, it also contains our
// own private key/certificate which will be presented in the TLS handshake.
// The peer's certificate verification can be overridden with the auth
// parameter.
class Cert
{
private:
std::shared_ptr<CA> peer_ca;
std::optional<std::string> peer_hostname;
mbedtls::X509Crt own_cert = nullptr;
mbedtls::PKContext own_pkey = nullptr;
bool has_own_cert;
// DEBUG ONLY
std::string cert_str;
Auth auth;
public:
Cert(
std::shared_ptr<CA> peer_ca_,
const std::optional<crypto::Pem>& own_cert_ = std::nullopt,
const std::optional<crypto::Pem>& own_pkey_ = std::nullopt,
CBuffer pw = nullb,
Auth auth_ = auth_default,
const std::optional<std::string>& peer_hostname_ = std::nullopt) :
peer_ca(peer_ca_),
peer_hostname(peer_hostname_),
has_own_cert(false),
auth(auth_)
{
auto tmp_cert = mbedtls::make_unique<mbedtls::X509Crt>();
auto tmp_pkey = mbedtls::make_unique<mbedtls::PKContext>();
if (own_cert_.has_value() && own_pkey_.has_value())
{
cert_str =
std::string((const char*)own_cert_->data(), own_cert_->size());
int rc = mbedtls_x509_crt_parse(
tmp_cert.get(), own_cert_->data(), own_cert_->size());
if (rc != 0)
{
throw std::logic_error(
"Could not parse certificate: " + error_string(rc));
}
rc = mbedtls_pk_parse_key(
tmp_pkey.get(), own_pkey_->data(), own_pkey_->size(), pw.p, pw.n);
if (rc != 0)
{
throw std::logic_error("Could not parse key: " + error_string(rc));
}
has_own_cert = true;
}
own_cert = std::move(tmp_cert);
own_pkey = std::move(tmp_pkey);
}
~Cert() {}
void use(mbedtls_ssl_context* ssl, mbedtls_ssl_config* cfg)
{
if (peer_hostname.has_value())
{
// Peer hostname is only checked against peer certificate (SAN
// extension) if it is set. This lets us connect to peers that present
// certificates with IPAddress in SAN field (mbedtls does not parse
// IPAddress in SAN field). This is OK since we check for peer CA
// endorsement.
mbedtls_ssl_set_hostname(ssl, peer_hostname->c_str());
}
if (peer_ca)
{
peer_ca->use(cfg);
}
if (auth != auth_default)
{
mbedtls_ssl_conf_authmode(cfg, authmode(auth));
}
if (has_own_cert)
{
mbedtls_ssl_conf_own_cert(cfg, own_cert.get(), own_pkey.get());
}
}
const mbedtls_x509_crt* raw()
{
return own_cert.get();
}
const std::string str() const
{
return cert_str;
}
private:
int authmode(Auth auth)
{
switch (auth)
{
case auth_none:
{
// Peer certificate is not checked
return MBEDTLS_SSL_VERIFY_NONE;
}
case auth_optional:
{
// Peer certificate is checked but handshake continues even if
// verification fails
return MBEDTLS_SSL_VERIFY_OPTIONAL;
}
case auth_required:
{
// Peer must present a valid certificate
return MBEDTLS_SSL_VERIFY_REQUIRED;
}
default:
{
}
}
return MBEDTLS_SSL_VERIFY_REQUIRED;
}
};
}

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

@ -1,22 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the Apache 2.0 License.
#pragma once
#include "context.h"
namespace tls
{
class Client : public Context
{
private:
std::shared_ptr<Cert> cert;
public:
Client(std::shared_ptr<Cert> cert_, bool dtls = false) :
Context(true, dtls),
cert(cert_)
{
cert->use(ssl.get(), cfg.get());
}
};
}

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

@ -1,129 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the Apache 2.0 License.
#pragma once
#include "cert.h"
#include "crypto/entropy.h"
#include "crypto/mbedtls/error_string.h"
#include "crypto/mbedtls/mbedtls_wrappers.h"
#include <memory>
using namespace crypto;
namespace tls
{
class Context
{
protected:
mbedtls::SSLContext ssl = nullptr;
mbedtls::SSLConfig cfg = nullptr;
crypto::EntropyPtr entropy;
#ifndef NO_STRICT_TLS_CIPHERSUITES
const int ciphersuites[2] = {
MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 0};
#endif
public:
Context(bool client, bool dgram) : entropy(crypto::create_entropy())
{
int rc = 0;
auto tmp_ssl = mbedtls::make_unique<mbedtls::SSLContext>();
auto tmp_cfg = mbedtls::make_unique<mbedtls::SSLConfig>();
mbedtls_ssl_conf_rng(
tmp_cfg.get(), entropy->get_rng(), entropy->get_data());
rc = mbedtls_ssl_config_defaults(
tmp_cfg.get(),
client ? MBEDTLS_SSL_IS_CLIENT : MBEDTLS_SSL_IS_SERVER,
dgram ? MBEDTLS_SSL_TRANSPORT_DATAGRAM : MBEDTLS_SSL_TRANSPORT_STREAM,
MBEDTLS_SSL_PRESET_DEFAULT);
if (rc != 0)
{
throw std::logic_error(fmt::format(
"mbedtls_ssl_config_defaults failed: {}", error_string(rc)));
}
#ifndef NO_STRICT_TLS_CIPHERSUITES
if (!client)
mbedtls_ssl_conf_ciphersuites(tmp_cfg.get(), ciphersuites);
#endif
// Require TLS 1.2
mbedtls_ssl_conf_min_version(
tmp_cfg.get(),
MBEDTLS_SSL_MAJOR_VERSION_3,
MBEDTLS_SSL_MINOR_VERSION_3);
rc = mbedtls_ssl_setup(tmp_ssl.get(), tmp_cfg.get());
if (rc != 0)
{
throw std::logic_error(
fmt::format("mbedtls_ssl_setup failed: {}", error_string(rc)));
}
ssl = std::move(tmp_ssl);
cfg = std::move(tmp_cfg);
}
virtual ~Context() {}
void set_bio(
void* enclave,
mbedtls_ssl_send_t send,
mbedtls_ssl_recv_t recv,
void (*dbg)(void*, int, const char*, int, const char*))
{
mbedtls_ssl_conf_dbg(cfg.get(), dbg, enclave);
mbedtls_ssl_set_bio(ssl.get(), enclave, send, recv, nullptr);
}
int handshake()
{
return mbedtls_ssl_handshake(ssl.get());
}
int read(uint8_t* buf, size_t len)
{
return mbedtls_ssl_read(ssl.get(), buf, len);
}
int write(const uint8_t* buf, size_t len)
{
return mbedtls_ssl_write(ssl.get(), buf, len);
}
int close()
{
return mbedtls_ssl_close_notify(ssl.get());
}
int verify_result()
{
return mbedtls_ssl_get_verify_result(ssl.get());
}
std::string get_verify_error()
{
std::vector<char> buf(512);
mbedtls_x509_crt_verify_info(
buf.data(), buf.size(), "Certificate verify failed: ", verify_result());
return std::string(buf.data(), buf.size());
}
virtual std::string host()
{
return {};
}
std::vector<uint8_t> peer_cert()
{
auto cert = mbedtls_ssl_get_peer_cert(ssl.get());
if (cert == nullptr)
return {};
return std::vector<uint8_t>(cert->raw.p, cert->raw.p + cert->raw.len);
}
};
}

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

@ -1,22 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the Apache 2.0 License.
#pragma once
#include "context.h"
namespace tls
{
class Server : public Context
{
private:
std::shared_ptr<Cert> cert;
public:
Server(std::shared_ptr<Cert> cert_, bool dtls = false) :
Context(false, dtls),
cert(cert_)
{
cert->use(ssl.get(), cfg.get());
}
};
}

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

@ -1,55 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the Apache 2.0 License.
#pragma once
#include <mbedtls/ctr_drbg.h>
#include <mbedtls/debug.h>
#include <mbedtls/entropy.h>
#include <mbedtls/entropy_poll.h>
#include <mbedtls/error.h>
#include <mbedtls/net_sockets.h>
#include <mbedtls/oid.h>
#include <mbedtls/rsa.h>
#include <mbedtls/sha256.h>
#include <mbedtls/ssl.h>
#include <mbedtls/x509.h>
#include <mbedtls/x509_crt.h>
#include <mbedtls/x509_csr.h>
// Macros provided to interface MbedTLS and OpenSSL on the same implementation.
#ifdef TLS_PROVIDER_IS_MBEDTLS
// These macros setup return values for when the connection is reading/writing
// and needs more data. In MbedTLS, the return value is straight WANT_READ and
// WANT_WRITE, which are negative and the API knows how to handle it.
# define TLS_READING MBEDTLS_ERR_SSL_WANT_READ
# define TLS_WRITING MBEDTLS_ERR_SSL_WANT_WRITE
// These macros are errors from read/write, including during handshake.
// Depending on the error, the connection needs to close with success, failure
// or auth-failure.
# define TLS_ERR_WANT_READ MBEDTLS_ERR_SSL_WANT_READ
# define TLS_ERR_WANT_WRITE MBEDTLS_ERR_SSL_WANT_WRITE
# define TLS_ERR_CONN_RESET MBEDTLS_ERR_NET_CONN_RESET
# define TLS_ERR_CONN_CLOSE_NOTIFY MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY
# define TLS_ERR_NEED_CERT MBEDTLS_ERR_SSL_NO_CLIENT_CERTIFICATE
# define TLS_ERR_PEER_VERIFY MBEDTLS_ERR_SSL_PEER_VERIFY_FAILED
# define TLS_ERR_X509_VERIFY MBEDTLS_ERR_X509_CERT_VERIFY_FAILED
namespace tls
{
/// Returns the error string from an error code
/// this is a copy of crypto's to all control via TLS_PROVIDER_IS_MBEDTLS
inline std::string error_string(int err)
{
constexpr size_t len = 256;
char buf[len];
mbedtls_strerror(err, buf, len);
if (strlen(buf) == 0)
{
return std::to_string(err);
}
return std::string(buf);
}
}
#endif

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

@ -1,70 +0,0 @@
# OpenSSL TLS Implementation
This is a TLS implementation using OpenSSL that mimics the existing MbedTLS
one in a similar fashion. Because of that, some structures and call backs
look odd and have some work-arounds to make it fit the current workflow.
Once we completely deprecate the MbedTLS implementation from CCF, we should
re-write the TLS implementation to fit the OpenSSL coding flow, which would
make it much simpler and easier to use.
## CAs and Certificates
In the MbedTLS world, certificates can be null and have methods to change
some configurations in the TLS config/session objects. There isn't a lot of
cross-over, so updating the config does the trick.
However, in OpenSSL, session objects (ssl) are created from config objects
(cfg) and inherit all its properties. Therefore, to emulate MbedTLS, we need
to do to the session object every action we do to the config object, which is
not only redundant, but could be unsafe, if the calls are slightly different.
### Validation
Certificate validation can be complex to handle if you can accept connections
with certificates or not, and if they come, when and how to validate.
MbedTLS is a lot more lenient on checks. For example, CAs are not tested for
validity of actually signing other certificates, while OpenSSL has extensive
checks, which can fail functionality that was previously passing.
For this reason, a number of extra checks in the OpenSSL side were disabled.
Once we get rid of MbedTLS we should revisit those checks again and improve
CCF's usage of TLS, and perhaps also creating weaker checks for non-CA
certificates, etc.
## Context
### BIOs
MbedTLS operates reads and writes solely via callbacks, with a buffer in the
session object acting as async I/O. This is in stark contrast with OpenSSL
which uses BIO objects to pass information back and forth, and only have
callbacks for debug or very specialized cases.
We had to implement callbacks and specialize our case, but it could really be
just done with BIOs between the ring buffer and the context, but we'd have to
change a lot of code outside of the TLS implementation to add that.
### Reads and Writes
Reading and writing in MbedTLS returns a positive value for success (number of
bytes written) or a negative value for error (pre-defined error codes) including
WANTS_READ and WANTS_WRITE.
In OpenSSL, those methods return 1 for success and 0 or -1 for errors (depending
on the version), with all errors, including WANTS_READ and WANTS_WRITE
accessible through `SSL_get_error`. This imposes a number of hacks needed to
mimic the MbedTLS implementation, including:
- Multiple `#define`s with common error messages in `tls.h`
- Having to negate the error code to match
- Multiple checks to `SSL_want_read` and `SSL_want_write`
### Error Handling
As discussed above, the error handling is slightly different and promotes
verbose code in OpenSSL's side.
Another example is `verify_result` and `get_verify_error` that is very long
in the OpenSSL implementation and two one-liners in MbedTLS.

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

@ -1,74 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the Apache 2.0 License.
#pragma once
#include "crypto/openssl/openssl_wrappers.h"
#include "crypto/pem.h"
#include "ds/buffer.h"
#include "ds/logger.h"
#include <exception>
#include <openssl/ssl.h>
#include <openssl/x509.h>
#include <openssl/x509v3.h>
namespace tls
{
class CA
{
private:
mutable crypto::OpenSSL::Unique_X509 ca;
mutable crypto::OpenSSL::Unique_X509_CRL crl;
public:
CA(CBuffer ca_ = nullb, CBuffer crl_ = nullb)
{
crypto::OpenSSL::Unique_X509 tmp_ca;
crypto::OpenSSL::Unique_X509_CRL tmp_crl;
if (ca_.n > 0)
{
crypto::Pem pem_ca(ca_);
LOG_TRACE_FMT("CA::ctor: PEM: {}", pem_ca.str());
BIO* certBio = BIO_new(BIO_s_mem());
BIO_write(certBio, pem_ca.data(), pem_ca.size());
X509* res = PEM_read_bio_X509(certBio, NULL, NULL, NULL);
BIO_free(certBio);
if (!res)
{
auto err_str = crypto::OpenSSL::error_string(ERR_get_error());
LOG_FAIL_FMT("CA::ctor: Could not parse CA: {}", err_str);
throw std::logic_error("Could not parse CA: " + err_str);
}
// The PEM_read function above checks the validity of the certificate,
// but not if it's a CA or cn be used as such. This is what MbedTLS
// checks, so we keep it simple here. Some code uses this as a
// "certificate check" not necessarily a CA check, so we need to keep it
// compatible.
// To cater to that usage, we should create a generic helper in crypto
// to do the certificate check and add X509_check_ca() here to be more
// robust on our verification.
tmp_ca.reset(res);
}
if (crl_.n > 0)
{
// We don't seem to be using CRL anywhere in CCF, so we should
// really remove this option once MbedTLS is gone.
LOG_FAIL_FMT("CA::ctor: Using CRL in OpenSSL CA");
throw std::logic_error("Using CRL in OpenSSL CA");
}
ca = std::move(tmp_ca);
crl = std::move(tmp_crl);
}
~CA() {}
void use(SSL* ssl, SSL_CTX* cfg)
{
SSL_CTX_use_certificate(cfg, ca);
SSL_use_certificate(ssl, ca);
}
};
}

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

@ -1,178 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the Apache 2.0 License.
#pragma once
#include "ca.h"
#include "ds/logger.h"
#include "tls/openssl/tls.h"
#include <cstring>
#include <memory>
#include <openssl/ssl.h>
#include <optional>
using namespace crypto;
namespace tls
{
enum Auth
{
auth_default,
auth_none,
auth_optional,
auth_required
};
// This class represents the authentication/authorization context for a TLS
// session. At least, it contains the peer's CA. At most, it also contains our
// own private key/certificate which will be presented in the TLS handshake.
// The peer's certificate verification can be overridden with the auth
// parameter.
class Cert
{
private:
std::shared_ptr<CA> peer_ca;
std::optional<std::string> peer_hostname;
crypto::OpenSSL::Unique_X509 own_cert;
crypto::OpenSSL::Unique_PKEY own_pkey;
bool has_own_cert;
Auth auth;
public:
Cert(
std::shared_ptr<CA> peer_ca_,
const std::optional<crypto::Pem>& own_cert_ = std::nullopt,
const std::optional<crypto::Pem>& own_pkey_ = std::nullopt,
CBuffer pw = nullb,
Auth auth_ = auth_default,
const std::optional<std::string>& peer_hostname_ = std::nullopt) :
peer_ca(peer_ca_),
peer_hostname(peer_hostname_),
has_own_cert(false),
auth(auth_)
{
crypto::OpenSSL::Unique_X509 tmp_cert;
crypto::OpenSSL::Unique_PKEY tmp_pkey;
if (own_cert_.has_value() && own_pkey_.has_value())
{
BIO* certBio = BIO_new(BIO_s_mem());
BIO_write(certBio, own_cert_->data(), own_cert_->size());
X509* cert = PEM_read_bio_X509(certBio, NULL, NULL, NULL);
BIO_free(certBio);
if (!cert)
{
auto err_str = tls::error_string(ERR_get_error());
LOG_FAIL_FMT("Cert::ctor: Could not parse certificate: {}", err_str);
throw std::logic_error("Could not parse certificate: " + err_str);
}
tmp_cert.reset(cert);
BIO* pkBio = BIO_new(BIO_s_mem());
BIO_write(pkBio, own_pkey_->data(), own_pkey_->size());
EVP_PKEY* pk = PEM_read_bio_PrivateKey(pkBio, NULL, NULL, NULL);
BIO_free(pkBio);
if (!pk)
{
auto err_str = tls::error_string(ERR_get_error());
LOG_FAIL_FMT("Cert::ctor: Could not parse key: {}", err_str);
throw std::logic_error("Could not parse key: " + err_str);
}
tmp_pkey.reset(pk);
has_own_cert = true;
}
own_cert = std::move(tmp_cert);
own_pkey = std::move(tmp_pkey);
if (pw.n)
{
// We don't seem to be using PW anywhere in CCF, so we should
// really remove this option once MbedTLS is gone.
LOG_FAIL_FMT("Cert::ctor: Unused password");
throw std::logic_error(
"Unused password: " + std::string((const char*)pw.p));
}
}
~Cert() {}
void use(SSL* ssl, SSL_CTX* cfg)
{
if (peer_hostname.has_value())
{
LOG_TRACE_FMT(
"Cert::use() : Hostname has value '{}'", peer_hostname->c_str());
// Peer hostname is only checked against peer certificate (SAN
// extension) if it is set. This lets us connect to peers that present
// certificates with IPAddress in SAN field (mbedtls does not parse
// IPAddress in SAN field). This is OK since we check for peer CA
// endorsement.
SSL_set1_host(ssl, peer_hostname->c_str());
}
if (peer_ca)
{
LOG_TRACE_FMT("Cert::use() : Peer CA use cfg");
peer_ca->use(ssl, cfg);
}
// Calling set_verify with SSL_VERIFY_PEER forces the handshake to request
// a peer certificate. The server always sends it to the client but not
// the other way around. Some code relies on the server doing that, so we
// set this here.
// We return 1 from the validation callback (a common patter in OpenSSL
// implementations) because we don't want to verify it here, just request
// it.
SSL_CTX_set_verify(
cfg, SSL_VERIFY_PEER, [](int precheck, x509_store_ctx_st* st) {
(void)precheck;
(void)st;
return 1;
});
SSL_set_verify(
ssl, SSL_VERIFY_PEER, [](int precheck, x509_store_ctx_st* st) {
(void)precheck;
(void)st;
return 1;
});
// The MBedTLS implementation adds some verification, but any
// further flags in OpenSSL's set_verify fail when MBedTLS doesn't.
// We still need to request the peer cert every time, even if it's empty,
// but it would be good to have some more strict checks on the actual
// certificate at this level without leaving it for later.
// This would probably be done after we remove MbedTLS and refactor
// OpenSSL TLS to match OpenSSL's behaviour.
if (has_own_cert)
{
LOG_TRACE_FMT("Cert::use() : Has own cert & PK");
// Chain of X509 certificates is 'nullptr', as we haven't established a
// chain yet. Overrides = 0, only sets cert&key once, since they don't
// change with repeated calls to use().
int rc = SSL_CTX_use_cert_and_key(cfg, own_cert, own_pkey, nullptr, 1);
if (!rc)
{
auto err_str = tls::error_string(ERR_get_error());
LOG_FAIL_FMT("Cert::ctor: Invalid CTX certificate or key: ", err_str);
throw std::logic_error("Invalid CTX certificate or key: " + err_str);
}
rc = SSL_use_cert_and_key(ssl, own_cert, own_pkey, nullptr, 1);
if (!rc)
{
auto err_str = tls::error_string(ERR_get_error());
LOG_FAIL_FMT("Cert::ctor: Invalid SSL certificate or key: ", err_str);
throw std::logic_error("Invalid SSL certificate or key: " + err_str);
}
}
}
const X509* raw()
{
return own_cert;
}
};
}

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

@ -1,22 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the Apache 2.0 License.
#pragma once
#include "context.h"
namespace tls
{
class Client : public Context
{
private:
std::shared_ptr<Cert> cert;
public:
Client(std::shared_ptr<Cert> cert_, bool dtls = false) :
Context(true, dtls),
cert(cert_)
{
cert->use(ssl, cfg);
}
};
}

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

@ -1,524 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the Apache 2.0 License.
#pragma once
#include "cert.h"
#include "crypto/base64.h"
#include "crypto/entropy.h"
#include "ds/logger.h"
#include "tls/openssl/tls.h"
#include <memory>
#include <openssl/bio.h>
#include <openssl/ssl.h>
using namespace crypto;
namespace tls
{
class Context
{
protected:
crypto::OpenSSL::Unique_SSL_CTX cfg;
crypto::OpenSSL::Unique_SSL ssl;
public:
Context(bool client, bool dtls) :
cfg(
dtls ? (client ? DTLS_client_method() : DTLS_server_method()) :
(client ? TLS_client_method() : TLS_server_method())),
ssl(cfg)
{
// Require at least TLS 1.2, support up to 1.3
SSL_CTX_set_min_proto_version(
cfg, dtls ? DTLS1_2_VERSION : TLS1_2_VERSION);
SSL_set_min_proto_version(ssl, dtls ? DTLS1_2_VERSION : TLS1_2_VERSION);
// Disable renegotiation to avoid DoS
SSL_CTX_set_options(
cfg,
SSL_OP_CIPHER_SERVER_PREFERENCE |
SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION |
SSL_OP_NO_RENEGOTIATION);
SSL_set_options(
ssl,
SSL_OP_CIPHER_SERVER_PREFERENCE |
SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION |
SSL_OP_NO_RENEGOTIATION);
// Set cipher for TLS 1.2 (TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256)
SSL_CTX_set_cipher_list(
cfg,
"ECDHE-ECDSA-AES256-GCM-SHA384:"
"ECDHE-ECDSA-AES128-GCM-SHA256");
SSL_set_cipher_list(
ssl,
"ECDHE-ECDSA-AES256-GCM-SHA384:"
"ECDHE-ECDSA-AES128-GCM-SHA256");
// Set cipher for TLS 1.3 (same as above)
SSL_CTX_set_ciphersuites(
cfg,
"TLS_AES_256_GCM_SHA384:"
"TLS_AES_128_GCM_SHA256");
SSL_set_ciphersuites(
ssl,
"TLS_AES_256_GCM_SHA384:"
"TLS_AES_128_GCM_SHA256");
// Restrict the curves to approved ones
SSL_CTX_set1_curves_list(cfg, "P-521:P-384");
SSL_set1_curves_list(ssl, "P-521:P-384");
// Initialise connection
if (client)
SSL_set_connect_state(ssl);
else
SSL_set_accept_state(ssl);
}
virtual ~Context() {}
void set_bio(
void* cb_obj,
BIO_callback_fn_ex send,
BIO_callback_fn_ex recv,
BIO_callback_fn_ex dbg)
{
// Read/Write BIOs will be used by TLS
BIO* rbio = BIO_new(BIO_s_mem());
BIO_set_mem_eof_return(rbio, -1);
BIO_set_callback_arg(rbio, (char*)cb_obj);
BIO_set_callback_ex(rbio, recv);
SSL_set0_rbio(ssl, rbio);
BIO* wbio = BIO_new(BIO_s_mem());
BIO_set_mem_eof_return(wbio, -1);
BIO_set_callback_arg(wbio, (char*)cb_obj);
BIO_set_callback_ex(wbio, send);
SSL_set0_wbio(ssl, wbio);
// We don't need debug callbacks and the other two already have
// enough debug messages. Once we get rid of MbedTLS, we can remove this
// argument.
(void)dbg;
}
int handshake()
{
if (SSL_is_init_finished(ssl))
return 0;
int rc = SSL_do_handshake(ssl);
// Success in OpenSSL is 1, MBed is 0
if (rc > 0)
{
LOG_TRACE_FMT("Context::handshake() : Success");
return 0;
}
// Want read/write needs special return
if (SSL_want_read(ssl))
{
return TLS_ERR_WANT_READ;
}
else if (SSL_want_write(ssl))
{
return TLS_ERR_WANT_WRITE;
}
// So does x509 validation
if (verify_result() != 0)
{
return TLS_ERR_X509_VERIFY;
}
// Everything else falls here.
LOG_TRACE_FMT("Context::handshake() : Error code {}", rc);
// As an MBedTLS emulation, we return negative for errors.
return -SSL_get_error(ssl, rc);
}
int read(uint8_t* buf, size_t len)
{
if (len == 0)
return 0;
size_t readbytes = 0;
int rc = SSL_read_ex(ssl, buf, len, &readbytes);
if (rc > 0)
{
return readbytes;
}
if (SSL_want_read(ssl))
{
return TLS_ERR_WANT_READ;
}
// Everything else falls here.
LOG_TRACE_FMT("Context::read() : Error code {}", rc);
// As an MBedTLS emulation, we return negative for errors.
return -SSL_get_error(ssl, rc);
}
int write(const uint8_t* buf, size_t len)
{
if (len == 0)
return 0;
size_t written = 0;
int rc = SSL_write_ex(ssl, buf, len, &written);
if (rc > 0)
{
return written;
}
if (SSL_want_write(ssl))
{
return TLS_ERR_WANT_WRITE;
}
// Everything else falls here.
LOG_TRACE_FMT("Context::write() : Error code {}", rc);
// As an MBedTLS emulation, we return negative for errors.
return -SSL_get_error(ssl, rc);
}
int close()
{
LOG_TRACE_FMT("Context::close() : Shutdown");
return SSL_shutdown(ssl);
}
// This is a hack to make it work like MBedTLS (with negative return
// values as error), and to differentiate in get_verify_error if the error
// is because we don't have a peer cert or something else.
// We may find that this is unnecessary (alongside all of the error messages
// in get_verify_error), but that's a cleanup that will need a bit more
// research.
#define TLS_ERR_X509_NO_PEER_CERT -1
#define TLS_ERR_X509_INVALID_RESULT -2
int verify_result()
{
if (SSL_get_verify_result(ssl) == X509_V_OK)
{
// Verify can return OK when no certificate is presented
// We want that to be an error
X509* cert = SSL_get_peer_certificate(ssl);
if (cert)
{
X509_free(cert);
return 0;
}
else
{
return TLS_ERR_X509_NO_PEER_CERT;
}
}
return TLS_ERR_X509_INVALID_RESULT;
}
std::string get_verify_error()
{
int rc = verify_result();
if (rc == TLS_ERR_X509_NO_PEER_CERT)
{
return "Certificate verify error: No peer certificate";
}
switch (rc)
{
case X509_V_ERR_UNSPECIFIED:
return "Unspecified error; should not happen.";
case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
return "The issuer certificate of a looked up certificate could not "
"be found. This normally means the list of trusted "
"certificates is not complete.";
case X509_V_ERR_UNABLE_TO_GET_CRL:
return "The CRL of a certificate could not be found.";
case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE:
return "The certificate signature could not be decrypted. This means "
"that the actual signature value could not be determined "
"rather than it not matching the expected value";
case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE:
return "The CRL signature could not be decrypted: this means that "
"the actual signature value could not be determined rather "
"than it not matching the expected value. Unused.";
case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY:
return "The public key in the certificate SubjectPublicKeyInfo could "
"not be read.";
case X509_V_ERR_CERT_SIGNATURE_FAILURE:
return "The signature of the certificate is invalid.";
case X509_V_ERR_CRL_SIGNATURE_FAILURE:
return "The signature of the certificate is invalid.";
case X509_V_ERR_CERT_NOT_YET_VALID:
return "The certificate is not yet valid: the notBefore date is "
"after the current time.";
case X509_V_ERR_CERT_HAS_EXPIRED:
return "The certificate has expired: that is the notAfter date is "
"before the current time.";
case X509_V_ERR_CRL_NOT_YET_VALID:
return "The CRL is not yet valid.";
case X509_V_ERR_CRL_HAS_EXPIRED:
return "The CRL has expired.";
case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
return "The certificate notBefore field contains an invalid time.";
case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
return "The certificate notAfter field contains an invalid time.";
case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD:
return "The CRL lastUpdate field contains an invalid time.";
case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD:
return "The CRL nextUpdate field contains an invalid time.";
case X509_V_ERR_OUT_OF_MEM:
return "An error occurred trying to allocate memory. This should "
"never happen.";
case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
return "The passed certificate is self-signed and the same "
"certificate cannot be found in the list of trusted "
"certificates.";
case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
return "The certificate chain could be built up using the untrusted "
"certificates but the root could not be found locally.";
case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
return "The issuer certificate could not be found: this occurs if "
"the issuer certificate of an untrusted certificate cannot be "
"found.";
case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
return "No signatures could be verified because the chain contains "
"only one certificate and it is not self signed.";
case X509_V_ERR_CERT_CHAIN_TOO_LONG:
return "The certificate chain length is greater than the supplied "
"maximum depth. Unused.";
case X509_V_ERR_CERT_REVOKED:
return "The certificate has been revoked.";
case X509_V_ERR_INVALID_CA:
return "A CA certificate is invalid. Either it is not a CA or its "
"extensions are not consistent with the supplied purpose.";
case X509_V_ERR_PATH_LENGTH_EXCEEDED:
return "The basicConstraints pathlength parameter has been exceeded.";
case X509_V_ERR_INVALID_PURPOSE:
return "The supplied certificate cannot be used for the specified "
"purpose.";
case X509_V_ERR_CERT_UNTRUSTED:
return "The root CA is not marked as trusted for the specified "
"purpose.";
case X509_V_ERR_CERT_REJECTED:
return "The root CA is marked to reject the specified purpose.";
case X509_V_ERR_SUBJECT_ISSUER_MISMATCH:
return "Not used as of OpenSSL 1.1.0 as a result of the deprecation "
"of the -issuer_checks option.";
case X509_V_ERR_AKID_SKID_MISMATCH:
return "Not used as of OpenSSL 1.1.0 as a result of the deprecation "
"of the -issuer_checks option.";
case X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH:
return "Not used as of OpenSSL 1.1.0 as a result of the deprecation "
"of the -issuer_checks option.";
case X509_V_ERR_KEYUSAGE_NO_CERTSIGN:
return "Not used as of OpenSSL 1.1.0 as a result of the deprecation "
"of the -issuer_checks option.";
case X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER:
return "Unable to get CRL issuer certificate.";
case X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION:
return "Unhandled critical extension.";
case X509_V_ERR_KEYUSAGE_NO_CRL_SIGN:
return "Key usage does not include CRL signing.";
case X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION:
return "Unhandled critical CRL extension.";
case X509_V_ERR_INVALID_NON_CA:
return "Invalid non-CA certificate has CA markings.";
case X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED:
return "Proxy path length constraint exceeded.";
case X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE:
return "Key usage does not include digital signature.";
case X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED:
return "Proxy certificates not allowed";
case X509_V_ERR_INVALID_EXTENSION:
return "Invalid or inconsistent certificate extension.";
case X509_V_ERR_INVALID_POLICY_EXTENSION:
return "Invalid or inconsistent certificate policy extension.";
case X509_V_ERR_NO_EXPLICIT_POLICY:
return "No explicit policy.";
case X509_V_ERR_DIFFERENT_CRL_SCOPE:
return "Different CRL scope.";
case X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE:
return "Unsupported extension feature.";
case X509_V_ERR_UNNESTED_RESOURCE:
return "RFC 3779 resource not subset of parent's resources.";
case X509_V_ERR_PERMITTED_VIOLATION:
return "Permitted subtree violation.";
case X509_V_ERR_EXCLUDED_VIOLATION:
return "Excluded subtree violation.";
case X509_V_ERR_SUBTREE_MINMAX:
return "Name constraints minimum and maximum not supported.";
case X509_V_ERR_APPLICATION_VERIFICATION:
return "Application verification failure. Unused.";
case X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE:
return "Unsupported name constraint type.";
case X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX:
return "Unsupported or invalid name constraint syntax.";
case X509_V_ERR_UNSUPPORTED_NAME_SYNTAX:
return "Unsupported or invalid name syntax.";
case X509_V_ERR_CRL_PATH_VALIDATION_ERROR:
return "CRL path validation error.";
case X509_V_ERR_PATH_LOOP:
return "Path loop.";
case X509_V_ERR_SUITE_B_INVALID_VERSION:
return "Suite B: certificate version invalid.";
case X509_V_ERR_SUITE_B_INVALID_ALGORITHM:
return "Suite B: invalid public key algorithm.";
case X509_V_ERR_SUITE_B_INVALID_CURVE:
return "Suite B: invalid ECC curve.";
case X509_V_ERR_SUITE_B_INVALID_SIGNATURE_ALGORITHM:
return "Suite B: invalid signature algorithm.";
case X509_V_ERR_SUITE_B_LOS_NOT_ALLOWED:
return "Suite B: curve not allowed for this LOS.";
case X509_V_ERR_SUITE_B_CANNOT_SIGN_P_384_WITH_P_256:
return "Suite B: cannot sign P-384 with P-256.";
case X509_V_ERR_HOSTNAME_MISMATCH:
return "Hostname mismatch.";
case X509_V_ERR_EMAIL_MISMATCH:
return "Email address mismatch.";
case X509_V_ERR_IP_ADDRESS_MISMATCH:
return "IP address mismatch.";
case X509_V_ERR_DANE_NO_MATCH:
return "DANE TLSA authentication is enabled";
case X509_V_ERR_EE_KEY_TOO_SMALL:
return "EE certificate key too weak.";
case X509_V_ERR_INVALID_CALL:
return "nvalid certificate verification context.";
case X509_V_ERR_STORE_LOOKUP:
return "Issuer certificate lookup error.";
case X509_V_ERR_NO_VALID_SCTS:
return "Certificate Transparency required";
case X509_V_ERR_PROXY_SUBJECT_NAME_VIOLATION:
return "Proxy subject name violation.";
case X509_V_ERR_OCSP_VERIFY_NEEDED:
return "Returned by the verify callback to indicate an OCSP "
"verification is needed.";
case X509_V_ERR_OCSP_VERIFY_FAILED:
return "Returned by the verify callback to indicate OCSP "
"verification failed.";
case X509_V_ERR_OCSP_CERT_UNKNOWN:
return "Returned by the verify callback to indicate that the "
"certificate is not recognized by the OCSP responder.";
}
return "";
}
virtual std::string host()
{
return {};
}
std::vector<uint8_t> peer_cert()
{
// Get the certificate into a BIO as DER
crypto::OpenSSL::Unique_X509 cert(
SSL_get_peer_certificate(ssl), /*check_null=*/false);
if (!cert)
{
LOG_TRACE_FMT("Empty peer cert");
return {};
}
crypto::OpenSSL::Unique_BIO bio;
if (!i2d_X509_bio(bio, cert))
{
LOG_TRACE_FMT("Can't convert X509 to DER");
return {};
}
// Get the total length of the DER representation
auto len = BIO_get_mem_data(bio, nullptr);
if (!len)
{
LOG_TRACE_FMT("Null X509 peer cert");
return {};
}
// Get the BIO memory pointer
BUF_MEM* ptr = nullptr;
if (!BIO_get_mem_ptr(bio, &ptr))
{
LOG_TRACE_FMT("Invalid X509 peer cert");
return {};
}
// Return its contents as a vector
auto ret = std::vector<uint8_t>(ptr->data, ptr->data + len);
return ret;
}
};
}

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

@ -1,22 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the Apache 2.0 License.
#pragma once
#include "context.h"
namespace tls
{
class Server : public Context
{
private:
std::shared_ptr<Cert> cert;
public:
Server(std::shared_ptr<Cert> cert_, bool dtls = false) :
Context(false, dtls),
cert(cert_)
{
cert->use(ssl, cfg);
}
};
}

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

@ -2,8 +2,21 @@
// Licensed under the Apache 2.0 License.
#pragma once
#ifdef TLS_PROVIDER_IS_MBEDTLS
# include "mbedtls/server.h"
#else
# include "openssl/server.h"
#endif
#include "context.h"
namespace tls
{
class Server : public Context
{
private:
std::shared_ptr<Cert> cert;
public:
Server(std::shared_ptr<Cert> cert_, bool dtls = false) :
Context(false, dtls),
cert(cert_)
{
cert->use(ssl, cfg);
}
};
}

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

@ -3,9 +3,7 @@
#include "crypto/certs.h"
#include "crypto/key_pair.h"
#include "crypto/verifier.h"
// These headers are temporary, until we have a single TLS implementation
#include "tls/mbedtls/tls.h"
#include "tls/openssl/tls.h"
#include "tls/tls.h"
#include <openssl/err.h>
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
@ -94,8 +92,6 @@ int recv(void* ctx, uint8_t* buf, size_t len)
return rc;
}
#ifndef TLS_PROVIDER_IS_MBEDTLS
// These are OpenSSL callbacks that call onto the MbedTLS ones. They have the
// same name but different signatures, so when using OpenSSL, these are the ones
// that set_bio() will take, and call the ones above by using the correct
@ -175,8 +171,6 @@ long recv(
return ret;
}
#endif
/// Performs a TLS handshake, looping until there's nothing more to read/write.
/// Returns 0 on success, throws a runtime error with SSL error str on failure.
int handshake(Context* ctx)
@ -261,7 +255,7 @@ unique_ptr<tls::Cert> get_dummy_cert(NetworkCA& net_ca, string name, Auth auth)
// Create a tls::Cert with the CA, the signed certificate and the private key
auto pk = kp->private_key_pem();
return make_unique<Cert>(move(ca), crt, pk, nullb, auth);
return make_unique<Cert>(move(ca), crt, pk, auth);
}
/// Helper to write past the maximum buffer (16k)

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

@ -2,8 +2,6 @@
// Licensed under the Apache 2.0 License.
#pragma once
// Macros provided to interface MbedTLS and OpenSSL on the same implementation.
#ifndef TLS_PROVIDER_IS_MBEDTLS
// These macros setup return values for when the connection is reading/writing
// and needs more data or has an error.
//
@ -17,27 +15,25 @@
// Some MbedTLS errors are not matched by OpenSSL, so we set some ridiculous
// negative number that will never match. This allows us to place the macro as
// a switch-case and not duplicate cases but also never match.
# define TLS_READING -SSL_READING
# define TLS_WRITING -SSL_WRITING
# define TLS_ERR_WANT_READ -SSL_ERROR_WANT_READ
# define TLS_ERR_WANT_WRITE -SSL_ERROR_WANT_WRITE
# define TLS_ERR_CONN_CLOSE_NOTIFY -SSL_ERROR_ZERO_RETURN
# define TLS_ERR_NEED_CERT -SSL_ERROR_WANT_X509_LOOKUP
#define TLS_READING -SSL_READING
#define TLS_WRITING -SSL_WRITING
#define TLS_ERR_WANT_READ -SSL_ERROR_WANT_READ
#define TLS_ERR_WANT_WRITE -SSL_ERROR_WANT_WRITE
#define TLS_ERR_CONN_CLOSE_NOTIFY -SSL_ERROR_ZERO_RETURN
#define TLS_ERR_NEED_CERT -SSL_ERROR_WANT_X509_LOOKUP
// No counterpart in OpenSSL
# define TLS_ERR_CONN_RESET INT_MIN
# define TLS_ERR_PEER_VERIFY INT_MIN + 1
# define TLS_ERR_X509_VERIFY INT_MIN + 2
#define TLS_ERR_CONN_RESET INT_MIN
#define TLS_ERR_PEER_VERIFY INT_MIN + 1
#define TLS_ERR_X509_VERIFY INT_MIN + 2
# include <openssl/err.h>
# include <string>
#include "crypto/openssl/openssl_wrappers.h"
#include <string>
namespace tls
{
/// Returns the error string from an error code
/// this is a copy of crypto's to allow control via TLS_PROVIDER_IS_MBEDTLS
inline std::string error_string(int ec)
{
return ERR_error_string((unsigned long)ec, nullptr);
return crypto::OpenSSL::error_string(ec);
}
}
#endif

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

@ -59,14 +59,6 @@ benchmark_specs = {
"D": "2048",
},
],
"digest_bench.csv": [
{
"_name": "mbedtls sha256 (/s)^",
"Suite": "mbedtls_digest_sha256",
"Benchmark": "SHA-256",
"D": "524288",
},
],
}
if __name__ == "__main__":