- 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
324 lines
12 KiB
C++
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_
|