Files
cariflex/tools/EVerest-main/lib/everest/tls/tests/tls_connection_test.hpp
Eric F d398a6ced2 Add extracted tools: CitrineOS, OpenOCPP, ShapeShifter
- CitrineOS core extracted (CSMS OCPP 2.0.1)
- OpenOCPP extracted (firmware OCPP 1.6J/2.0.1)
- ShapeShifter library installed (pip install -e)
- ShapeShifter specification extracted
- EVerest extracted

TODO updated with progress
2026-06-08 00:38:27 -04:00

324 lines
12 KiB
C++

// SPDX-License-Identifier: Apache-2.0
// Copyright 2024 Pionix GmbH and Contributors to EVerest
#ifndef TLS_CONNECTION_TEST_HPP_
#define TLS_CONNECTION_TEST_HPP_
#include "extensions/helpers.hpp"
#include <everest/tls/openssl_util.hpp>
#include <array>
#include <chrono>
#include <csignal>
#include <everest/tls/tls.hpp>
#include <gtest/gtest.h>
#include <memory>
#include <openssl/ssl.h>
#include <thread>
#include <unistd.h>
#include <everest/util/enum/EnumFlags.hpp>
using namespace std::chrono_literals;
namespace {
using tls::status_request::ClientStatusRequestV2;
// ----------------------------------------------------------------------------
// set up code
struct ClientStatusRequestV2Test : public ClientStatusRequestV2 {
enum class flags_t : std::uint8_t {
status_request_cb,
status_request,
status_request_v2,
connected,
last = connected,
};
everest::lib::util::AtomicEnumFlags<flags_t>& flags;
ClientStatusRequestV2Test() = delete;
explicit ClientStatusRequestV2Test(everest::lib::util::AtomicEnumFlags<flags_t>& flag_ref) : flags(flag_ref) {
}
int status_request_cb(tls::Ssl* ctx) override {
/*
* This callback is called when status_request or status_request_v2 extensions
* were present in the Client Hello. It doesn't mean that the extension is in
* the Server Hello SSL_get_tlsext_status_ocsp_resp() returns -1 in that case
*/
int result{1};
const unsigned char* response{nullptr};
const auto total_length = SSL_get_tlsext_status_ocsp_resp(ctx, &response);
flags.set(flags_t::status_request_cb);
if ((response != nullptr) && (total_length > 0) && (total_length <= std::numeric_limits<std::int32_t>::max())) {
switch (response[0]) {
case 0x30: // a status_request response
flags.set(flags_t::status_request);
if (!print_ocsp_response(stdout, response, total_length)) {
result = 0;
}
break;
case 0x00: // a status_request_v2 response
{
flags.set(flags_t::status_request_v2);
// multiple responses
auto remaining = static_cast<std::int32_t>(total_length);
const unsigned char* ptr{response};
while (remaining >= 3) {
const auto len = tls::uint24(ptr);
tls::update_position(ptr, remaining, 3);
// print_ocsp_response updates tmp_p
auto* tmp_p = ptr;
const auto res = print_ocsp_response(stdout, tmp_p, len);
tls::update_position(ptr, remaining, len);
if (!res || (ptr != tmp_p)) {
result = 0;
remaining = -1;
}
}
if (remaining != 0) {
result = 0;
}
break;
}
default:
break;
}
}
return result;
}
};
struct ClientTest : public tls::Client {
using flags_t = ClientStatusRequestV2Test::flags_t;
everest::lib::util::AtomicEnumFlags<flags_t> flags;
ClientTest() : tls::Client(std::unique_ptr<ClientStatusRequestV2>(new ClientStatusRequestV2Test(flags))) {
}
void reset() {
flags.reset();
}
};
void handler(tls::Server::ConnectionPtr&& con) {
if (con->accept() == tls::Connection::result_t::success) {
std::uint32_t count{0};
std::array<std::byte, 1024> buffer{};
bool bExit = false;
while (!bExit) {
std::size_t readbytes = 0;
std::size_t writebytes = 0;
switch (con->read(buffer.data(), buffer.size(), readbytes)) {
case tls::Connection::result_t::success:
switch (con->write(buffer.data(), readbytes, writebytes)) {
case tls::Connection::result_t::success:
break;
case tls::Connection::result_t::timeout:
case tls::Connection::result_t::closed:
default:
bExit = true;
break;
}
break;
case tls::Connection::result_t::timeout:
count++;
if (count > 10) {
bExit = true;
}
break;
case tls::Connection::result_t::closed:
default:
bExit = true;
break;
}
}
con->shutdown();
}
}
void run_server(tls::Server& server) {
server.serve(&handler);
}
class TlsTest : public testing::Test {
protected:
using flags_t = ClientTest::flags_t;
tls::Server server;
tls::Server::config_t server_config;
std::thread server_thread;
ClientTest client;
tls::Client::config_t client_config;
static void SetUpTestSuite() {
struct sigaction action;
std::memset(&action, 0, sizeof(action));
action.sa_handler = SIG_IGN;
sigaction(SIGPIPE, &action, nullptr);
tls::Server::configure_signal_handler(SIGUSR1);
}
void SetUp() override {
server_config.cipher_list = "ECDHE-ECDSA-AES128-SHA256";
// server_config.ciphersuites = "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384";
server_config.ciphersuites = "";
auto& ref0 = server_config.chains.emplace_back();
ref0.certificate_chain_file = "server_chain.pem";
ref0.private_key_file = "server_priv.pem";
ref0.trust_anchor_file = "server_root_cert.pem";
ref0.ocsp_response_files = {"ocsp_response.der", "ocsp_response.der"};
auto& ref1 = server_config.chains.emplace_back();
ref1.certificate_chain_file = "alt_server_chain.pem";
ref1.private_key_file = "alt_server_priv.pem";
ref1.trust_anchor_file = "alt_server_root_cert.pem";
ref1.ocsp_response_files = {"ocsp_response.der", "ocsp_response.der"};
// server_config.verify_locations_file = "client_root_cert.pem";
server_config.host = "127.0.0.1";
server_config.service = "8444";
server_config.ipv6_only = false;
server_config.verify_client = false;
server_config.io_timeout_ms = 1000; // no lower than 200ms, valgrind need much higher
client_config.cipher_list = "ECDHE-ECDSA-AES128-SHA256";
// client_config.ciphersuites = "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384";
// client_config.certificate_chain_file = "client_chain.pem";
// client_config.private_key_file = "client_priv.pem";
client_config.verify_locations_file = "server_root_cert.pem";
client_config.io_timeout_ms = 1000;
client_config.verify_server = true;
client_config.status_request = false;
client_config.status_request_v2 = false;
client.reset();
}
void TearDown() override {
server.stop();
server.wait_stopped();
if (server_thread.joinable()) {
server_thread.join();
}
}
void start(const tls::Server::ConfigurationCallback& init_ssl = nullptr) {
using state_t = tls::Server::state_t;
const auto res = server.init(server_config, init_ssl);
if ((res == state_t::init_complete) || (res == state_t::init_socket)) {
server_thread = std::thread(&run_server, std::ref(server));
server.wait_running();
}
}
void start(const std::function<void(tls::Server::ConnectionPtr&& con)>& handler) {
using state_t = tls::Server::state_t;
const auto res = server.init(server_config, nullptr);
if ((res == state_t::init_complete) || (res == state_t::init_socket)) {
server_thread = std::thread([this, handler]() { this->server.serve(handler); });
server.wait_running();
}
}
void connect(const std::function<void(tls::Client::ConnectionPtr& con)>& handler = nullptr) {
client.init(client_config);
client.reset();
// localhost works in some cases but not in the CI pipeline for IPv6
// use ip6-localhost
auto connection = client.connect("127.0.0.1", "8444", false, 1000);
if (handler == nullptr) {
if (connection) {
if (connection->connect() == tls::Connection::result_t::success) {
set(ClientTest::flags_t::connected);
connection->shutdown();
}
}
} else {
handler(connection);
}
}
void set(flags_t flag) {
client.flags.set(flag);
}
[[nodiscard]] bool is_set(flags_t flag) const {
return client.flags.is_set(flag);
}
[[nodiscard]] bool is_reset(flags_t flag) const {
return client.flags.is_reset(flag);
}
void add_ta_cert_hash(const char* filename) {
openssl::sha_1_digest_t digest;
auto certs = openssl::load_certificates(filename);
for (const auto& ta : certs) {
if (openssl::certificate_sha_1(digest, ta.get())) {
client_config.trusted_ca_keys_data.cert_sha1_hash.push_back(digest);
}
}
}
void add_ta_key_hash(const char* filename) {
openssl::sha_1_digest_t digest;
auto certs = openssl::load_certificates(filename);
for (const auto& ta : certs) {
if (openssl::certificate_subject_public_key_sha_1(digest, ta.get())) {
client_config.trusted_ca_keys_data.key_sha1_hash.push_back(digest);
}
}
}
void add_ta_name(const char* filename) {
auto certs = openssl::load_certificates(filename);
for (const auto& ta : certs) {
auto res = openssl::certificate_subject_der(ta.get());
if (res) {
client_config.trusted_ca_keys_data.x509_name.emplace_back(std::move(res));
}
}
}
};
class TlsTestTpm : public TlsTest {
protected:
void SetUp() override {
server_config.cipher_list = "ECDHE-ECDSA-AES128-SHA256";
// server_config.ciphersuites = "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384";
server_config.ciphersuites = "";
auto& ref0 = server_config.chains.emplace_back();
ref0.certificate_chain_file = "tpm_pki/server_chain.pem";
ref0.private_key_file = "tpm_pki/server_priv.pem";
ref0.trust_anchor_file = "tpm_pki/server_root_cert.pem";
ref0.ocsp_response_files = {"ocsp_response.der", "ocsp_response.der"};
// auto& ref1 = server_config.chains.emplace_back();
// ref1.certificate_chain_file = "alt_server_chain.pem";
// ref1.private_key_file = "alt_server_priv.pem";
// ref1.trust_anchor_file = "alt_server_root_cert.pem";
// ref1.ocsp_response_files = {"ocsp_response.der", "ocsp_response.der"};
server_config.host = "127.0.0.1";
server_config.service = "8444";
server_config.ipv6_only = false;
server_config.verify_client = false;
server_config.io_timeout_ms = 1000; // no lower than 200ms, valgrind need much higher
client_config.cipher_list = "ECDHE-ECDSA-AES128-SHA256";
// client_config.ciphersuites = "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384";
// client_config.certificate_chain_file = "client_chain.pem";
// client_config.private_key_file = "client_priv.pem";
client_config.verify_locations_file = "tpm_pki/server_root_cert.pem";
client_config.io_timeout_ms = 1000;
client_config.verify_server = true;
client_config.status_request = false;
client_config.status_request_v2 = false;
client.reset();
}
};
} // namespace
#endif // TLS_CONNECTION_TEST_HPP_