Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ The [*ws*](https://github.com/machinezone/IXWebSocket/tree/master/ws) folder cou

## Windows note

To use the network system on Windows, you need to initialize it once with *WSAStartup()* and clean it up with *WSACleanup()*. We have helpers for that which you can use, see below. This init would typically take place in your main function.
To use the network system on Windows, you need to initialize it once with *WSAStartup()* and clean it up with *WSACleanup()*. We have helpers for that which you can use, see below. This init would typically take place in your main function. On every platform, *ix::uninitNetSystem()* also releases process-global TLS state (mbedTLS PSA crypto, with mbedTLS >= 3.6), so call it only after all sockets are closed.

```cpp
#include <ixwebsocket/IXNetSystem.h>
Expand Down
9 changes: 9 additions & 0 deletions ixwebsocket/IXNetSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@
#include "IXNetSystem.h"
#include <cstdint>
#include <cstdio>
#ifdef IXWEBSOCKET_USE_MBED_TLS
#include <mbedtls/version.h>
#if MBEDTLS_VERSION_MAJOR >= 4 || (MBEDTLS_VERSION_MAJOR == 3 && MBEDTLS_VERSION_MINOR >= 6)
#include <psa/crypto.h>
#endif
#endif
#ifdef _WIN32
#ifndef EAFNOSUPPORT
#define EAFNOSUPPORT 102
Expand Down Expand Up @@ -38,6 +44,9 @@ namespace ix

bool uninitNetSystem()
{
#if defined(IXWEBSOCKET_USE_MBED_TLS) && (MBEDTLS_VERSION_MAJOR >= 4 || (MBEDTLS_VERSION_MAJOR == 3 && MBEDTLS_VERSION_MINOR >= 6))
mbedtls_psa_crypto_free();
#endif
#ifdef _WIN32
int err = WSACleanup();
return err == 0;
Expand Down
1 change: 1 addition & 0 deletions ixwebsocket/IXNetSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ namespace ix
#endif

bool initNetSystem();
// Also releases process-global TLS (PSA) state; call only after all sockets are closed.
bool uninitNetSystem();

int poll(struct pollfd* fds, nfds_t nfds, int timeout, void** event);
Expand Down
33 changes: 23 additions & 10 deletions ixwebsocket/IXSocketMbedTLS.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,19 @@ namespace ix
{
std::lock_guard<std::mutex> lock(_mutex);

mbedtls_ssl_init(&_ssl);
mbedtls_ssl_config_init(&_conf);
if (!_mbedtlsInitialized)
{
_mbedtlsInitialized = true;
mbedtls_ssl_init(&_ssl);
mbedtls_ssl_config_init(&_conf);
#if MBEDTLS_VERSION_MAJOR < 4
mbedtls_ctr_drbg_init(&_ctr_drbg);
mbedtls_entropy_init(&_entropy);
mbedtls_ctr_drbg_init(&_ctr_drbg);
mbedtls_entropy_init(&_entropy);
#endif
mbedtls_x509_crt_init(&_cacert);
mbedtls_x509_crt_init(&_cert);
mbedtls_pk_init(&_pkey);
mbedtls_x509_crt_init(&_cacert);
mbedtls_x509_crt_init(&_cert);
mbedtls_pk_init(&_pkey);
}
// Initialize the PSA Crypto API for mbedTLS 3.6+ and all 4.x releases.
// See: https://github.com/Mbed-TLS/mbedtls/blob/development/docs/use-psa-crypto.md
#if MBEDTLS_VERSION_MAJOR >= 4 || (MBEDTLS_VERSION_MAJOR == 3 && MBEDTLS_VERSION_MINOR >= 6)
Expand Down Expand Up @@ -111,6 +115,15 @@ namespace ix
initMBedTLS();
std::lock_guard<std::mutex> lock(_mutex);

#if MBEDTLS_VERSION_MAJOR >= 4 || (MBEDTLS_VERSION_MAJOR == 3 && MBEDTLS_VERSION_MINOR >= 6)
psa_status_t status = psa_crypto_init();
if (status != PSA_SUCCESS)
{
errMsg = "PSA crypto init failed: " + std::to_string((int) status);
return false;
}
#endif

#if MBEDTLS_VERSION_MAJOR < 4
const char* pers = "IXSocketMbedTLS";
if (mbedtls_ctr_drbg_seed(&_ctr_drbg,
Expand Down Expand Up @@ -328,9 +341,9 @@ namespace ix
mbedtls_x509_crt_free(&_cacert);
mbedtls_x509_crt_free(&_cert);
mbedtls_pk_free(&_pkey);
#if MBEDTLS_VERSION_MAJOR >= 4 || (MBEDTLS_VERSION_MAJOR == 3 && MBEDTLS_VERSION_MINOR >= 6)
mbedtls_psa_crypto_free();
#endif
_mbedtlsInitialized = false;
// PSA crypto state is process-global; it is released in ix::uninitNetSystem(),
// not per socket.

Socket::close();
}
Expand Down
1 change: 1 addition & 0 deletions ixwebsocket/IXSocketMbedTLS.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ namespace ix

std::mutex _mutex;
SocketTLSOptions _tlsOptions;
bool _mbedtlsInitialized = false;

bool init(const std::string& host, bool isClient, std::string& errMsg);
void initMBedTLS();
Expand Down
6 changes: 6 additions & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,12 @@ if (USE_ZLIB)
)
endif()

if (USE_TLS AND USE_MBED_TLS)
list(APPEND TEST_TARGET_NAMES
IXSocketMbedTLSPSATest
)
endif()

# Ping test fails intermittently, disabling them for now
# IXWebSocketPingTest.cpp
# IXWebSocketPingTimeoutTest.cpp
Expand Down
93 changes: 93 additions & 0 deletions test/IXSocketMbedTLSPSATest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
* IXSocketMbedTLSPSATest.cpp
*
* PSA crypto state is process-global: closing one SocketMbedTLS must not
* tear it down under other live sockets.
*/

#ifdef IXWEBSOCKET_USE_MBED_TLS

#include <catch_amalgamated.hpp>
#include <ixwebsocket/IXNetSystem.h>
#include <mbedtls/version.h>

using namespace ix;

#if MBEDTLS_VERSION_MAJOR >= 4 || (MBEDTLS_VERSION_MAJOR == 3 && MBEDTLS_VERSION_MINOR >= 6)

#include <iostream>
#include <ixwebsocket/IXSocketMbedTLS.h>
#include <psa/crypto.h>

namespace
{
// Hashing works without PSA init, so probe the RNG and a key slot instead.
bool psaStateWorks()
{
uint8_t bytes[16];
psa_status_t status = psa_generate_random(bytes, sizeof(bytes));
if (status != PSA_SUCCESS)
{
std::cerr << "psa_generate_random failed: " << (int) status << std::endl;
return false;
}

psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
psa_set_key_type(&attributes, PSA_KEY_TYPE_HMAC);
psa_set_key_bits(&attributes, 128);
psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_SIGN_MESSAGE);
psa_set_key_algorithm(&attributes, PSA_ALG_HMAC(PSA_ALG_SHA_256));

psa_key_id_t key = 0;
status = psa_import_key(&attributes, bytes, sizeof(bytes), &key);
psa_reset_key_attributes(&attributes);
if (status != PSA_SUCCESS)
{
std::cerr << "psa_import_key failed: " << (int) status << std::endl;
return false;
}

return psa_destroy_key(key) == PSA_SUCCESS;
}
} // namespace

TEST_CASE("SocketMbedTLS close does not tear down process-global PSA state", "[socket_mbedtls]")
{
SocketMbedTLS a(SocketTLSOptions{});
REQUIRE(psaStateWorks());

{
SocketMbedTLS b(SocketTLSOptions{});
b.close();
}
REQUIRE(psaStateWorks());

a.close();

SocketMbedTLS c(SocketTLSOptions{});
REQUIRE(psaStateWorks());
}

TEST_CASE("uninitNetSystem releases PSA state and allows re-init", "[socket_mbedtls]")
{
REQUIRE(initNetSystem());
{
SocketMbedTLS a(SocketTLSOptions{});
a.close();
}
REQUIRE(uninitNetSystem());

REQUIRE(psa_crypto_init() == PSA_SUCCESS);
REQUIRE(psaStateWorks());
}

#else // mbedTLS < 3.6

TEST_CASE("SocketMbedTLS PSA lifetime (not applicable below mbedTLS 3.6)", "[socket_mbedtls]")
{
SUCCEED("mbedTLS < 3.6 has no PSA state to release");
}

#endif // mbedTLS >= 3.6

#endif // IXWEBSOCKET_USE_MBED_TLS