From 2007bdfe1d093470dec7ba842608d77d60b4daab Mon Sep 17 00:00:00 2001 From: Avijit Dey Date: Fri, 1 Dec 2023 14:47:17 +0100 Subject: [PATCH] Add to test for testing TLS client & server --- .../appl/include/create_diagnostic_client.h | 2 +- .../boost-support/socket/tcp/tcp_server.cpp | 1 + .../boost-support/socket/tcp/tls_client.cpp | 8 +- .../lib/boost-support/socket/tcp/tls_client.h | 11 +- .../boost-support/socket/tcp/tls_server.cpp | 89 ++++---- test/test_case/tls_test.cpp | 190 +++++++++++++++++- tools/generate_tls_certificates.sh | 14 +- 7 files changed, 264 insertions(+), 51 deletions(-) diff --git a/diag-client-lib/appl/include/create_diagnostic_client.h b/diag-client-lib/appl/include/create_diagnostic_client.h index 350c87d1..eff94a0f 100644 --- a/diag-client-lib/appl/include/create_diagnostic_client.h +++ b/diag-client-lib/appl/include/create_diagnostic_client.h @@ -26,7 +26,7 @@ class DiagClient; * Unique pointer to diag client object * @implements DiagClientLib-Library-Support, DiagClientLib-ComParam-Settings */ -std::unique_ptr CreateDiagnosticClient(std::string_view diag_client_config_path); +std::unique_ptr CreateDiagnosticClient(std::string_view diag_client_config_path); } // namespace client } // namespace diag diff --git a/diag-client-lib/lib/boost-support/socket/tcp/tcp_server.cpp b/diag-client-lib/lib/boost-support/socket/tcp/tcp_server.cpp index 04320fcd..26b56c5b 100644 --- a/diag-client-lib/lib/boost-support/socket/tcp/tcp_server.cpp +++ b/diag-client-lib/lib/boost-support/socket/tcp/tcp_server.cpp @@ -26,6 +26,7 @@ CreateTcpServerSocket::CreateTcpServerSocket(std::string_view local_ip_address, msg << "Tcp Socket Accepter created at " << "<" << local_ip_address << "," << local_port_num << ">"; }); + tcp_accepter_->listen(); } CreateTcpServerSocket::TcpServerConnection CreateTcpServerSocket::GetTcpServerConnection( diff --git a/diag-client-lib/lib/boost-support/socket/tcp/tls_client.cpp b/diag-client-lib/lib/boost-support/socket/tcp/tls_client.cpp index b827546a..1b4f7cc8 100644 --- a/diag-client-lib/lib/boost-support/socket/tcp/tls_client.cpp +++ b/diag-client-lib/lib/boost-support/socket/tcp/tls_client.cpp @@ -17,7 +17,7 @@ namespace socket { namespace tcp { TlsClientSocket::TlsClientSocket(std::string_view local_ip_address, std::uint16_t local_port_num, - TcpHandlerRead tcp_handler_read) + TcpHandlerRead tcp_handler_read, std::string_view ca_certification_path) : local_ip_address_{local_ip_address}, local_port_num_{local_port_num}, io_service{}, @@ -27,12 +27,16 @@ TlsClientSocket::TlsClientSocket(std::string_view local_ip_address, std::uint16_ running_{false}, cond_var_{}, mutex_{}, + thread_{}, tcp_handler_read_{std::move(tcp_handler_read)} { // Set verification mode tls_socket_.set_verify_mode(boost::asio::ssl::verify_peer); // Set the verification callback tls_socket_.set_verify_callback( [](bool pre_verified, boost::asio::ssl::verify_context &ctx) noexcept -> bool { return true; }); + // Load the root CA certificates + io_ssl_context_.load_verify_file(std::string{ca_certification_path}); + // Start thread to receive messages thread_ = std::thread([this]() { std::unique_lock lck(mutex_); @@ -65,7 +69,7 @@ core_type::Result TlsClientSocket::Open() { // Open the socket GetNativeTcpSocket().open(Tcp::v4(), ec); if (ec.value() == boost::system::errc::success) { - // reuse address + // Re-use address GetNativeTcpSocket().set_option(boost::asio::socket_base::reuse_address{true}); // Set socket to non blocking GetNativeTcpSocket().non_blocking(false); diff --git a/diag-client-lib/lib/boost-support/socket/tcp/tls_client.h b/diag-client-lib/lib/boost-support/socket/tcp/tls_client.h index 23d55282..80dfa516 100644 --- a/diag-client-lib/lib/boost-support/socket/tcp/tls_client.h +++ b/diag-client-lib/lib/boost-support/socket/tcp/tls_client.h @@ -52,7 +52,8 @@ class TlsClientSocket final { * @param[in] tcp_handler_read * The handler to send received data to user */ - TlsClientSocket(std::string_view local_ip_address, std::uint16_t local_port_num, TcpHandlerRead tcp_handler_read); + TlsClientSocket(std::string_view local_ip_address, std::uint16_t local_port_num, TcpHandlerRead tcp_handler_read, + std::string_view ca_certification_path); /** * @brief Destruct an instance of TcpClientSocket @@ -162,14 +163,14 @@ class TlsClientSocket final { std::condition_variable cond_var_; /** - * @brief The thread itself + * @brief mutex to lock critical section */ - std::thread thread_; + std::mutex mutex_; /** - * @brief mutex to lock critical section + * @brief The thread itself */ - std::mutex mutex_; + std::thread thread_; /** * @brief Store the handler diff --git a/diag-client-lib/lib/boost-support/socket/tcp/tls_server.cpp b/diag-client-lib/lib/boost-support/socket/tcp/tls_server.cpp index 5e5b7b72..22f86c53 100644 --- a/diag-client-lib/lib/boost-support/socket/tcp/tls_server.cpp +++ b/diag-client-lib/lib/boost-support/socket/tcp/tls_server.cpp @@ -54,9 +54,12 @@ std::optional TlsServerSocket::GetTcpServerConnection(TcpHa } TcpServerConnection::TcpServerConnection(boost::asio::io_context &io_context, TcpHandlerRead tcp_handler_read) - : io_ssl_context_{boost::asio::ssl::context::tlsv12_client}, + : io_ssl_context_{boost::asio::ssl::context::tlsv12_server}, tls_socket_{io_context, io_ssl_context_}, - tcp_handler_read_{std::move(tcp_handler_read)} {} + tcp_handler_read_{std::move(tcp_handler_read)} { + io_ssl_context_.use_certificate_chain_file("../../../tools/openssl/DiagClientLib.crt"); + io_ssl_context_.use_private_key_file("../../../tools/openssl/DiagClientLib.key", boost::asio::ssl::context::pem); +} TcpServerConnection::TlsStream::lowest_layer_type &TcpServerConnection::GetSocket() { return tls_socket_.lowest_layer(); @@ -64,8 +67,8 @@ TcpServerConnection::TlsStream::lowest_layer_type &TcpServerConnection::GetSocke core_type::Result TcpServerConnection::Transmit( TcpMessageConstPtr tcp_tx_message) { - core_type::Result result{TcpErrorCode::kGenericError}; TcpErrorCodeType ec{}; + core_type::Result result{TcpErrorCode::kGenericError}; boost::asio::write( tls_socket_, @@ -88,47 +91,57 @@ core_type::Result TcpServerConnection:: } bool TcpServerConnection::ReceivedMessage() { - TcpErrorCodeType ec; + TcpErrorCodeType ec{}; bool connection_closed{false}; - // create and reserve the buffer - TcpMessage::BufferType rx_buffer{}; - rx_buffer.resize(kDoipheadrSize); - // start blocking read to read Header first - boost::asio::read(tls_socket_, boost::asio::buffer(&rx_buffer[0], kDoipheadrSize), ec); - // Check for error + // Perform TLS handshake + tls_socket_.handshake(boost::asio::ssl::stream_base::server, ec); + if (ec.value() == boost::system::errc::success) { - // read the next bytes to read - std::uint32_t const read_next_bytes = [&rx_buffer]() noexcept -> std::uint32_t { - return static_cast((static_cast(rx_buffer[4u] << 24u) & 0xFF000000) | - (static_cast(rx_buffer[5u] << 16u) & 0x00FF0000) | - (static_cast(rx_buffer[6u] << 8u) & 0x0000FF00) | - (static_cast(rx_buffer[7u] & 0x000000FF))); - }(); - // reserve the buffer - rx_buffer.resize(kDoipheadrSize + std::size_t(read_next_bytes)); - boost::asio::read(tls_socket_, boost::asio::buffer(&rx_buffer[kDoipheadrSize], read_next_bytes), ec); - - // all message received, transfer to upper layer - Tcp::endpoint endpoint_{GetSocket().remote_endpoint()}; - TcpMessagePtr tcp_rx_message{ - std::make_unique(endpoint_.address().to_string(), endpoint_.port(), std::move(rx_buffer))}; - common::logger::LibBoostLogger::GetLibBoostLogger().GetLogger().LogDebug( - __FILE__, __LINE__, __func__, [endpoint_](std::stringstream &msg) { - msg << "Tcp Message received from " - << "<" << endpoint_.address().to_string() << "," << endpoint_.port() << ">"; - }); - // send data to upper layer - tcp_handler_read_(std::move(tcp_rx_message)); - } else if (ec.value() == boost::asio::error::eof) { - common::logger::LibBoostLogger::GetLibBoostLogger().GetLogger().LogDebug( - __FILE__, __LINE__, __func__, - [ec](std::stringstream &msg) { msg << "Remote Disconnected with: " << ec.message(); }); - connection_closed = true; + // Create and reserve the buffer + TcpMessage::BufferType rx_buffer{}; + rx_buffer.resize(kDoipheadrSize); + // Start blocking read to read Header first + boost::asio::read(tls_socket_, boost::asio::buffer(&rx_buffer[0], kDoipheadrSize), ec); + // Check for error + if (ec.value() == boost::system::errc::success) { + // Read the next bytes to read + std::uint32_t const read_next_bytes = [&rx_buffer]() noexcept -> std::uint32_t { + return static_cast((static_cast(rx_buffer[4u] << 24u) & 0xFF000000) | + (static_cast(rx_buffer[5u] << 16u) & 0x00FF0000) | + (static_cast(rx_buffer[6u] << 8u) & 0x0000FF00) | + (static_cast(rx_buffer[7u] & 0x000000FF))); + }(); + // reserve the buffer + rx_buffer.resize(kDoipheadrSize + std::size_t(read_next_bytes)); + boost::asio::read(tls_socket_, boost::asio::buffer(&rx_buffer[kDoipheadrSize], read_next_bytes), ec); + + // all message received, transfer to upper layer + Tcp::endpoint endpoint_{GetSocket().remote_endpoint()}; + TcpMessagePtr tcp_rx_message{ + std::make_unique(endpoint_.address().to_string(), endpoint_.port(), std::move(rx_buffer))}; + common::logger::LibBoostLogger::GetLibBoostLogger().GetLogger().LogDebug( + __FILE__, __LINE__, __func__, [endpoint_](std::stringstream &msg) { + msg << "Tcp Message received from " + << "<" << endpoint_.address().to_string() << "," << endpoint_.port() << ">"; + }); + // send data to upper layer + tcp_handler_read_(std::move(tcp_rx_message)); + } else if (ec.value() == boost::asio::error::eof) { + common::logger::LibBoostLogger::GetLibBoostLogger().GetLogger().LogDebug( + __FILE__, __LINE__, __func__, + [ec](std::stringstream &msg) { msg << "Remote Disconnected with: " << ec.message(); }); + connection_closed = true; + } else { + common::logger::LibBoostLogger::GetLibBoostLogger().GetLogger().LogError( + __FILE__, __LINE__, __func__, + [ec](std::stringstream &msg) { msg << "Remote Disconnected with undefined error: " << ec.message(); }); + connection_closed = true; + } } else { common::logger::LibBoostLogger::GetLibBoostLogger().GetLogger().LogError( __FILE__, __LINE__, __func__, - [ec](std::stringstream &msg) { msg << "Remote Disconnected with undefined error: " << ec.message(); }); + [ec](std::stringstream &msg) { msg << "Tls server handshake with host failed with error: " << ec.message(); }); connection_closed = true; } return connection_closed; diff --git a/test/test_case/tls_test.cpp b/test/test_case/tls_test.cpp index dd5bcd7f..efd9ecfa 100644 --- a/test/test_case/tls_test.cpp +++ b/test/test_case/tls_test.cpp @@ -8,14 +8,198 @@ #include #include +#include + #include "socket/tcp/tls_client.h" #include "socket/tcp/tls_server.h" +namespace doip_client { +namespace { + +// TLS client Tcp Ip Address +constexpr std::string_view TlsClientIpAddress{"172.16.25.127"}; + +// TLS Server Tcp Ip Address +constexpr std::string_view TlsServerIpAddress{"172.16.25.128"}; + +// TLS port number +std::uint16_t TlsPort{3496u}; + +constexpr std::string_view RootCACertificatePath{"../../../tools/openssl/rootCA.pem"}; + +class TlsClient final { + public: + TlsClient(std::string_view local_ip_address, std::uint16_t local_port_num) + : tls_client_socket_{ + local_ip_address, local_port_num, + [this](boost_support::socket::tcp::TcpMessagePtr message) { OnMessageReceived(std::move(message)); }, + RootCACertificatePath} { + // Open the socket + tls_client_socket_.Open(); + } + + bool Connect(std::string_view host_ip_address, std::uint16_t host_port_num) { + return tls_client_socket_.ConnectToHost(host_ip_address, host_port_num).HasValue(); + } + + bool Disconnect() { return tls_client_socket_.DisconnectFromHost().HasValue(); } + + bool Transmit(boost_support::socket::tcp::TcpMessageConstPtr tx_message) { + return tls_client_socket_.Transmit(std::move(tx_message)).HasValue(); + } + + ~TlsClient() { tls_client_socket_.Destroy(); } + + private: + /** + * @brief Mock method invoked on reception of message + */ + MOCK_METHOD(void, OnMessageReceived, (boost_support::socket::tcp::TcpMessagePtr), ()); + + /** + * @brief TLS client socket + */ + boost_support::socket::tcp::TlsClientSocket tls_client_socket_; +}; + +class TlsServer final { + public: + TlsServer(std::string_view local_ip_address, std::uint16_t local_port_num) + : tls_server_socket_{local_ip_address, local_port_num}, + tcp_server_connection_{}, + exit_request_{false}, + running_{false}, + cond_var_{}, + mutex_{}, + thread_{} { + // Start thread to receive messages + thread_ = std::thread([this]() { + std::unique_lock lck(mutex_); + while (!exit_request_) { + if (!running_) { + cond_var_.wait(lck, [this]() { return exit_request_ || running_; }); + CreateTcpServerConnection(); + } + if (!exit_request_.load()) { + if (running_) { + lck.unlock(); + // start receiving messages on accepted message + if (tcp_server_connection_->ReceivedMessage()) { + tcp_server_connection_->Shutdown(); + // connection is closed + running_ = false; + exit_request_ = true; + } + lck.lock(); + } + } + } + }); + std::this_thread::sleep_for(std::chrono::milliseconds{200}); + } + + ~TlsServer() { + exit_request_ = true; + running_ = false; + cond_var_.notify_all(); + thread_.join(); + } + + void StartAcceptingNewConnection() { + // Start accepting new connection + { // start reading + std::lock_guard lock{mutex_}; + running_ = true; + } + cond_var_.notify_all(); + } + + private: + /** + * @brief Mock method invoked on reception of message + */ + MOCK_METHOD(void, OnMessageReceived, (boost_support::socket::tcp::TcpMessagePtr), ()); + + /** + * @brief Function to create the server connection + */ + void CreateTcpServerConnection() { + tcp_server_connection_ = tls_server_socket_.GetTcpServerConnection( + [this](boost_support::socket::tcp::TcpMessagePtr message) { OnMessageReceived(std::move(message)); }); + // terminate if no connection received + assert(tcp_server_connection_); + } + + private: + /** + * @brief TLS client socket + */ + boost_support::socket::tcp::TlsServerSocket tls_server_socket_; + + /** + * @brief Tcp server connection + */ + std::optional tcp_server_connection_; + + /** + * @brief Flag to terminate the thread + */ + std::atomic_bool exit_request_; + + /** + * @brief Flag to start the thread + */ + std::atomic_bool running_; + + /** + * @brief Conditional variable to block the thread + */ + std::condition_variable cond_var_; + + /** + * @brief mutex to lock critical section + */ + std::mutex mutex_; + + /** + * @brief The thread itself + */ + std::thread thread_; +}; + +} // namespace + /** * @brief Fixture to test the TLS connection between client and server with supported cipher list - * */ class TLSFixture : public ::testing::Test { protected: - private: -}; \ No newline at end of file + void SetUp() override { + tls_server_.emplace(TlsServerIpAddress, TlsPort); + tls_client_.emplace(TlsClientIpAddress, 0u); + } + + void TearDown() override { + tls_client_.reset(); + tls_server_.reset(); + } + + protected: + /** + * @brief TLS server + */ + std::optional tls_server_{}; + + /** + * @brief TLS client + */ + std::optional tls_client_{}; +}; + +TEST_F(TLSFixture, SendAndReceiveMessage) { + tls_server_->StartAcceptingNewConnection(); + tls_client_->Connect(TlsServerIpAddress, TlsPort); + std::this_thread::sleep_for(std::chrono::seconds(5)); +} + +} // namespace doip_client \ No newline at end of file diff --git a/tools/generate_tls_certificates.sh b/tools/generate_tls_certificates.sh index 958eb373..1491bdf5 100755 --- a/tools/generate_tls_certificates.sh +++ b/tools/generate_tls_certificates.sh @@ -1,7 +1,8 @@ #! /bin/bash -# References:- 1. https://devopscube.com/create-self-signed-certificates-openssl/, -# 2. https://mariadb.com/docs/server/security/data-in-transit-encryption/create-self-signed-certificates-keys-openssl/ +# References:- +# 1. https://devopscube.com/create-self-signed-certificates-openssl/, +# 2. https://mariadb.com/docs/server/security/data-in-transit-encryption/create-self-signed-certificates-keys-openssl/ # Modification is done as per this project DOMAIN=DiagClientLib @@ -70,3 +71,12 @@ openssl x509 -req \ -CAcreateserial -out ${DOMAIN}.crt \ -days 365 \ -sha256 -extfile cert.conf + + +# Convert from CRT to PEM format +openssl x509 -in rootCA.crt -out rootCA.pem +openssl x509 -in ${DOMAIN}.crt -out ${DOMAIN}.pem + +# Verify the certificates +openssl verify -CAfile rootCA.pem DiagClientLib.crt +