From 876b7440016d4394b5892e028a609ec57d1fc67e Mon Sep 17 00:00:00 2001 From: Dave Johansen Date: Fri, 17 Jul 2026 16:05:20 -0600 Subject: [PATCH 1/4] Add Mbed TLS 4.x support with PSA Crypto compatibility Auto-detect Mbed TLS 4.x and adapt the TLS backend for PSA init, removed CTR-DRBG/RNG callbacks, tfpsacrypto linking, and TLS 1.3 session-ticket retries so 2.x/3.x/4.x all work through CPPHTTPLIB_MBEDTLS_SUPPORT. Co-authored-by: Cursor --- .github/workflows/test.yaml | 48 ++++++- .github/workflows/test_proxy.yaml | 35 ++++- CMakeLists.txt | 16 ++- README.md | 2 +- docs-src/pages/en/tour/05-tls-setup.md | 2 +- docs-src/pages/ja/tour/05-tls-setup.md | 2 +- httplib.h | 176 +++++++++++++++++++++---- meson.build | 6 +- test/Makefile | 14 +- test/test.cc | 3 +- 10 files changed, 258 insertions(+), 46 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index ea50f94cd3..ba860b8265 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -79,7 +79,7 @@ jobs: strategy: fail-fast: false matrix: - tls_backend: [openssl, mbedtls, wolfssl] + tls_backend: [openssl, mbedtls, mbedtls4, wolfssl] name: ubuntu (${{ matrix.tls_backend }}) steps: - name: checkout @@ -91,9 +91,36 @@ jobs: - name: install OpenSSL if: matrix.tls_backend == 'openssl' run: sudo apt-get install -y libssl-dev - - name: install Mbed TLS + - name: install Mbed TLS 3.x if: matrix.tls_backend == 'mbedtls' run: sudo apt-get install -y libmbedtls-dev + - name: cache Mbed TLS 4.x prefix + if: matrix.tls_backend == 'mbedtls4' + id: mbedtls4-cache + uses: actions/cache@v4 + with: + path: ${{ runner.temp }}/mbedtls-4.2.0 + key: mbedtls-4.2.0-${{ runner.os }}-cmake + - name: install Mbed TLS 4.x + if: matrix.tls_backend == 'mbedtls4' + env: + MBEDTLS_VERSION: "4.2.0" + MBEDTLS_PREFIX: ${{ runner.temp }}/mbedtls-4.2.0 + run: | + set -euo pipefail + if [ ! -f "${MBEDTLS_PREFIX}/lib/libmbedtls.so" ] && [ ! -f "${MBEDTLS_PREFIX}/lib/libmbedtls.a" ]; then + curl -fsSL -o mbedtls.tar.bz2 \ + "https://github.com/Mbed-TLS/mbedtls/releases/download/mbedtls-${MBEDTLS_VERSION}/mbedtls-${MBEDTLS_VERSION}.tar.bz2" + tar -xjf mbedtls.tar.bz2 + cmake -S "mbedtls-${MBEDTLS_VERSION}" -B mbedtls-build \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_INSTALL_PREFIX="${MBEDTLS_PREFIX}" \ + -DENABLE_TESTING=OFF \ + -DENABLE_PROGRAMS=OFF + cmake --build mbedtls-build -j"$(nproc)" + cmake --install mbedtls-build + fi + echo "MBEDTLS_DIR=${MBEDTLS_PREFIX}" >> "$GITHUB_ENV" - name: install wolfSSL if: matrix.tls_backend == 'wolfssl' run: sudo apt-get install -y libwolfssl-dev @@ -103,7 +130,7 @@ jobs: env: LSAN_OPTIONS: suppressions=lsan_suppressions.txt - name: build and run tests (Mbed TLS) - if: matrix.tls_backend == 'mbedtls' + if: matrix.tls_backend == 'mbedtls' || matrix.tls_backend == 'mbedtls4' # Run mbedTLS shards with reduced parallelism — under ASAN+mbedTLS the # default 4 shards overload CI runners enough that timing-sensitive # ServerTest cases flake on first-request keep-alive reuse. @@ -365,14 +392,17 @@ jobs: strategy: fail-fast: false matrix: - tls_backend: [openssl, mbedtls, wolfssl] + tls_backend: [openssl, mbedtls, mbedtls4, wolfssl] name: macos (${{ matrix.tls_backend }}) steps: - name: checkout uses: actions/checkout@v4 - - name: install Mbed TLS + - name: install Mbed TLS 3.x if: matrix.tls_backend == 'mbedtls' run: brew install mbedtls@3 + - name: install Mbed TLS 4.x + if: matrix.tls_backend == 'mbedtls4' + run: brew install mbedtls - name: install wolfSSL if: matrix.tls_backend == 'wolfssl' run: brew install wolfssl @@ -382,11 +412,15 @@ jobs: env: LSAN_OPTIONS: suppressions=lsan_suppressions.txt - name: build and run tests (Mbed TLS) - if: matrix.tls_backend == 'mbedtls' + if: matrix.tls_backend == 'mbedtls' || matrix.tls_backend == 'mbedtls4' # macOS runners under ASAN+mbedTLS still flake at SHARDS=2 (rapid # bind/connect on the fixture's fixed port races on the slower # macos-latest runner). Serialize fully here; ubuntu stays at 2. - run: cd test && make test_split_mbedtls && SHARDS=1 make test_mbedtls_parallel + run: | + if [ "${{ matrix.tls_backend }}" = "mbedtls4" ]; then + export MBEDTLS_DIR="$(brew --prefix mbedtls)" + fi + cd test && make test_split_mbedtls && SHARDS=1 make test_mbedtls_parallel - name: build and run tests (wolfSSL) if: matrix.tls_backend == 'wolfssl' run: cd test && make test_split_wolfssl && make test_wolfssl_parallel diff --git a/.github/workflows/test_proxy.yaml b/.github/workflows/test_proxy.yaml index cf09432a02..eea04d4f4c 100644 --- a/.github/workflows/test_proxy.yaml +++ b/.github/workflows/test_proxy.yaml @@ -8,7 +8,7 @@ jobs: if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name strategy: matrix: - tls_backend: [openssl, mbedtls] + tls_backend: [openssl, mbedtls, mbedtls4] name: proxy (${{ matrix.tls_backend }}) steps: @@ -21,9 +21,36 @@ jobs: - name: Install OpenSSL if: matrix.tls_backend == 'openssl' run: sudo apt-get install -y libssl-dev - - name: Install Mbed TLS + - name: Install Mbed TLS 3.x if: matrix.tls_backend == 'mbedtls' run: sudo apt-get install -y libmbedtls-dev + - name: Cache Mbed TLS 4.x prefix + if: matrix.tls_backend == 'mbedtls4' + id: mbedtls4-cache + uses: actions/cache@v4 + with: + path: ${{ runner.temp }}/mbedtls-4.2.0 + key: mbedtls-4.2.0-${{ runner.os }}-cmake + - name: Install Mbed TLS 4.x + if: matrix.tls_backend == 'mbedtls4' + env: + MBEDTLS_VERSION: "4.2.0" + MBEDTLS_PREFIX: ${{ runner.temp }}/mbedtls-4.2.0 + run: | + set -euo pipefail + if [ ! -f "${MBEDTLS_PREFIX}/lib/libmbedtls.so" ] && [ ! -f "${MBEDTLS_PREFIX}/lib/libmbedtls.a" ]; then + curl -fsSL -o mbedtls.tar.bz2 \ + "https://github.com/Mbed-TLS/mbedtls/releases/download/mbedtls-${MBEDTLS_VERSION}/mbedtls-${MBEDTLS_VERSION}.tar.bz2" + tar -xjf mbedtls.tar.bz2 + cmake -S "mbedtls-${MBEDTLS_VERSION}" -B mbedtls-build \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_INSTALL_PREFIX="${MBEDTLS_PREFIX}" \ + -DENABLE_TESTING=OFF \ + -DENABLE_PROGRAMS=OFF + cmake --build mbedtls-build -j"$(nproc)" + cmake --install mbedtls-build + fi + echo "MBEDTLS_DIR=${MBEDTLS_PREFIX}" >> "$GITHUB_ENV" - name: Run proxy tests (OpenSSL) if: matrix.tls_backend == 'openssl' @@ -31,7 +58,7 @@ jobs: env: COMPOSE_FILE: docker-compose.yml:docker-compose.ci.yml - name: Run proxy tests (Mbed TLS) - if: matrix.tls_backend == 'mbedtls' + if: matrix.tls_backend == 'mbedtls' || matrix.tls_backend == 'mbedtls4' run: cd test && make proxy_mbedtls env: - COMPOSE_FILE: docker-compose.yml:docker-compose.ci.yml \ No newline at end of file + COMPOSE_FILE: docker-compose.yml:docker-compose.ci.yml diff --git a/CMakeLists.txt b/CMakeLists.txt index d3d8c29491..3db2b70cf7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -216,6 +216,20 @@ elseif(HTTPLIB_USE_MBEDTLS_IF_AVAILABLE) set(HTTPLIB_IS_USING_MBEDTLS ${MbedTLS_FOUND}) endif() +# Mbed TLS 4.x renamed MbedTLS::mbedcrypto -> MbedTLS::tfpsacrypto +set(_HTTPLIB_MBEDTLS_LIBS "") +if(HTTPLIB_IS_USING_MBEDTLS) + list(APPEND _HTTPLIB_MBEDTLS_LIBS MbedTLS::mbedtls) + if(TARGET MbedTLS::mbedx509) + list(APPEND _HTTPLIB_MBEDTLS_LIBS MbedTLS::mbedx509) + endif() + if(TARGET MbedTLS::tfpsacrypto) + list(APPEND _HTTPLIB_MBEDTLS_LIBS MbedTLS::tfpsacrypto) + elseif(TARGET MbedTLS::mbedcrypto) + list(APPEND _HTTPLIB_MBEDTLS_LIBS MbedTLS::mbedcrypto) + endif() +endif() + if(HTTPLIB_REQUIRE_ZLIB) find_package(ZLIB REQUIRED) set(HTTPLIB_IS_USING_ZLIB TRUE) @@ -377,7 +391,7 @@ target_link_libraries(${PROJECT_NAME} ${_INTERFACE_OR_PUBLIC} $<$:OpenSSL::SSL> $<$:OpenSSL::Crypto> $<$:wolfssl::wolfssl> - $<$:MbedTLS::mbedtls> + ${_HTTPLIB_MBEDTLS_LIBS} ) # Set the definitions to enable optional features diff --git a/README.md b/README.md index 9a6bebbdab..497f8d8d7a 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,7 @@ cpp-httplib supports multiple TLS backends through an abstraction layer: | Backend | Define | Libraries | Notes | | :------ | :----- | :-------- | :---- | | OpenSSL | `CPPHTTPLIB_OPENSSL_SUPPORT` | `libssl`, `libcrypto` | [3.0 or later](https://www.openssl.org/policies/releasestrat.html) required | -| Mbed TLS | `CPPHTTPLIB_MBEDTLS_SUPPORT` | `libmbedtls`, `libmbedx509`, `libmbedcrypto` | 2.x and 3.x supported (auto-detected) | +| Mbed TLS | `CPPHTTPLIB_MBEDTLS_SUPPORT` | `libmbedtls`, `libmbedx509`, `libmbedcrypto` (2.x/3.x) or `libtfpsacrypto` (4.x) | 2.x, 3.x, and 4.x supported (auto-detected) | | wolfSSL | `CPPHTTPLIB_WOLFSSL_SUPPORT` | `libwolfssl` | 5.x supported; must build with `--enable-opensslall` | > [!NOTE] diff --git a/docs-src/pages/en/tour/05-tls-setup.md b/docs-src/pages/en/tour/05-tls-setup.md index 40eee3ce7e..860dcbc359 100644 --- a/docs-src/pages/en/tour/05-tls-setup.md +++ b/docs-src/pages/en/tour/05-tls-setup.md @@ -76,7 +76,7 @@ cpp-httplib also supports Mbed TLS and wolfSSL in addition to OpenSSL. You can s | Backend | Macro | Libraries to link | | :--- | :--- | :--- | | OpenSSL | `CPPHTTPLIB_OPENSSL_SUPPORT` | `libssl`, `libcrypto` | -| Mbed TLS | `CPPHTTPLIB_MBEDTLS_SUPPORT` | `libmbedtls`, `libmbedx509`, `libmbedcrypto` | +| Mbed TLS | `CPPHTTPLIB_MBEDTLS_SUPPORT` | `libmbedtls`, `libmbedx509`, `libmbedcrypto` (2.x/3.x) or `libtfpsacrypto` (4.x) | | wolfSSL | `CPPHTTPLIB_WOLFSSL_SUPPORT` | `libwolfssl` | This tour assumes OpenSSL, but the API is the same regardless of which backend you choose. diff --git a/docs-src/pages/ja/tour/05-tls-setup.md b/docs-src/pages/ja/tour/05-tls-setup.md index ab6b8d1436..b26c607427 100644 --- a/docs-src/pages/ja/tour/05-tls-setup.md +++ b/docs-src/pages/ja/tour/05-tls-setup.md @@ -76,7 +76,7 @@ cpp-httplibはOpenSSL以外にも、Mbed TLSとwolfSSLに対応しています | バックエンド | マクロ定義 | リンクするライブラリ | | :--- | :--- | :--- | | OpenSSL | `CPPHTTPLIB_OPENSSL_SUPPORT` | `libssl`, `libcrypto` | -| Mbed TLS | `CPPHTTPLIB_MBEDTLS_SUPPORT` | `libmbedtls`, `libmbedx509`, `libmbedcrypto` | +| Mbed TLS | `CPPHTTPLIB_MBEDTLS_SUPPORT` | `libmbedtls`, `libmbedx509`, `libmbedcrypto`(2.x/3.x)または `libtfpsacrypto`(4.x) | | wolfSSL | `CPPHTTPLIB_WOLFSSL_SUPPORT` | `libwolfssl` | このTourではOpenSSLを前提に進めますが、APIはどのバックエンドでも共通です。 diff --git a/httplib.h b/httplib.h index 7f9813e7f9..6430579c9d 100644 --- a/httplib.h +++ b/httplib.h @@ -420,18 +420,31 @@ using socket_t = int; #endif // CPPHTTPLIB_OPENSSL_SUPPORT #ifdef CPPHTTPLIB_MBEDTLS_SUPPORT -#include -#include +#if defined(__has_include) +#if __has_include() +#include +#elif __has_include() +#include +#endif +#else +#include +#endif #include -#include #include #include #include +#include +#include +#if MBEDTLS_VERSION_MAJOR >= 4 +#include +#else +#include +#include +#include #include #include #include -#include -#include +#endif #ifdef _WIN32 #include #ifdef _MSC_VER @@ -444,7 +457,10 @@ using socket_t = int; #endif #endif -// Mbed TLS 3.x API compatibility +// Mbed TLS version API compatibility +#if MBEDTLS_VERSION_MAJOR >= 4 +#define CPPHTTPLIB_MBEDTLS_V4 +#endif #if MBEDTLS_VERSION_MAJOR >= 3 #define CPPHTTPLIB_MBEDTLS_V3 #endif @@ -3417,13 +3433,15 @@ bool is_field_value(const std::string &s); namespace tls { namespace impl { -// Mbed TLS context wrapper (holds config, entropy, DRBG, CA chain, own -// cert/key). This struct is accessible via tls::impl for use in SSL context -// setup callbacks (cast ctx_t to tls::impl::MbedTlsContext*). +// Mbed TLS context wrapper (holds config, optional entropy/DRBG on <4.x, CA +// chain, own cert/key). This struct is accessible via tls::impl for use in SSL +// context setup callbacks (cast ctx_t to tls::impl::MbedTlsContext*). struct MbedTlsContext { mbedtls_ssl_config conf; +#ifndef CPPHTTPLIB_MBEDTLS_V4 mbedtls_entropy_context entropy; mbedtls_ctr_drbg_context ctr_drbg; +#endif mbedtls_x509_crt ca_chain; mbedtls_x509_crt own_cert; mbedtls_pk_context own_key; @@ -9058,9 +9076,36 @@ inline std::string hash_to_hex(const unsigned char (&hash)[N]) { } } // namespace +// Ensure PSA Crypto is initialized once for Mbed TLS 4.x. Do not call +// mbedtls_psa_crypto_free(); PSA is process-global and may be shared. +inline bool ensure_mbedtls_psa_crypto() { +#ifdef CPPHTTPLIB_MBEDTLS_V4 + static std::once_flag once; + static bool ok = false; + std::call_once(once, []() { ok = (psa_crypto_init() == PSA_SUCCESS); }); + return ok; +#else + return true; +#endif +} + +#ifdef CPPHTTPLIB_MBEDTLS_V4 +inline bool psa_hash_to_buf(psa_algorithm_t alg, const std::string &s, + unsigned char *hash, size_t hash_size) { + if (!ensure_mbedtls_psa_crypto()) { return false; } + size_t out_len = 0; + return psa_hash_compute(alg, reinterpret_cast(s.data()), + s.size(), hash, hash_size, + &out_len) == PSA_SUCCESS && + out_len == hash_size; +} +#endif + inline std::string MD5(const std::string &s) { unsigned char hash[16]; -#ifdef CPPHTTPLIB_MBEDTLS_V3 +#ifdef CPPHTTPLIB_MBEDTLS_V4 + if (!psa_hash_to_buf(PSA_ALG_MD5, s, hash, sizeof(hash))) { return {}; } +#elif defined(CPPHTTPLIB_MBEDTLS_V3) mbedtls_md5(reinterpret_cast(s.c_str()), s.size(), hash); #else @@ -9072,7 +9117,9 @@ inline std::string MD5(const std::string &s) { inline std::string SHA_256(const std::string &s) { unsigned char hash[32]; -#ifdef CPPHTTPLIB_MBEDTLS_V3 +#ifdef CPPHTTPLIB_MBEDTLS_V4 + if (!psa_hash_to_buf(PSA_ALG_SHA_256, s, hash, sizeof(hash))) { return {}; } +#elif defined(CPPHTTPLIB_MBEDTLS_V3) mbedtls_sha256(reinterpret_cast(s.c_str()), s.size(), hash, 0); #else @@ -9084,7 +9131,9 @@ inline std::string SHA_256(const std::string &s) { inline std::string SHA_512(const std::string &s) { unsigned char hash[64]; -#ifdef CPPHTTPLIB_MBEDTLS_V3 +#ifdef CPPHTTPLIB_MBEDTLS_V4 + if (!psa_hash_to_buf(PSA_ALG_SHA_512, s, hash, sizeof(hash))) { return {}; } +#elif defined(CPPHTTPLIB_MBEDTLS_V3) mbedtls_sha512(reinterpret_cast(s.c_str()), s.size(), hash, 0); #else @@ -17924,11 +17973,29 @@ inline int &mbedtls_last_error() { return err; } +// TLS 1.3 NewSessionTicket is a non-fatal notification (especially on Mbed TLS +// 4.x where it is signaled by default). Treat it as "try again". +inline bool mbedtls_should_retry(int ret) { + if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE) { + return true; + } +#if defined(MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET) + if (ret == MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET) { return true; } +#endif + return false; +} + // Helper to map Mbed TLS error to ErrorCode inline ErrorCode map_mbedtls_error(int ret, int &out_errno) { if (ret == 0) { return ErrorCode::Success; } if (ret == MBEDTLS_ERR_SSL_WANT_READ) { return ErrorCode::WantRead; } if (ret == MBEDTLS_ERR_SSL_WANT_WRITE) { return ErrorCode::WantWrite; } +#if defined(MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET) + // Not fatal; callers that see WantRead will retry the operation. + if (ret == MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET) { + return ErrorCode::WantRead; + } +#endif if (ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) { return ErrorCode::PeerClosed; } @@ -17994,8 +18061,10 @@ inline int mbedtls_net_recv_cb(void *ctx, unsigned char *buf, size_t len) { // MbedTlsContext constructor/destructor implementations inline MbedTlsContext::MbedTlsContext() { mbedtls_ssl_config_init(&conf); +#ifndef CPPHTTPLIB_MBEDTLS_V4 mbedtls_entropy_init(&entropy); mbedtls_ctr_drbg_init(&ctr_drbg); +#endif mbedtls_x509_crt_init(&ca_chain); mbedtls_x509_crt_init(&own_cert); mbedtls_pk_init(&own_key); @@ -18005,8 +18074,10 @@ inline MbedTlsContext::~MbedTlsContext() { mbedtls_pk_free(&own_key); mbedtls_x509_crt_free(&own_cert); mbedtls_x509_crt_free(&ca_chain); +#ifndef CPPHTTPLIB_MBEDTLS_V4 mbedtls_ctr_drbg_free(&ctr_drbg); mbedtls_entropy_free(&entropy); +#endif mbedtls_ssl_config_free(&conf); } @@ -18080,6 +18151,13 @@ inline ctx_t create_client_context() { ctx->is_server = false; +#ifdef CPPHTTPLIB_MBEDTLS_V4 + if (!detail::ensure_mbedtls_psa_crypto()) { + delete ctx; + return nullptr; + } + int ret = 0; +#else // Seed the random number generator const char *pers = "httplib_client"; int ret = mbedtls_ctr_drbg_seed( @@ -18090,6 +18168,7 @@ inline ctx_t create_client_context() { delete ctx; return nullptr; } +#endif // Set up SSL config for client ret = mbedtls_ssl_config_defaults(&ctx->conf, MBEDTLS_SSL_IS_CLIENT, @@ -18101,8 +18180,10 @@ inline ctx_t create_client_context() { return nullptr; } - // Set random number generator +#ifndef CPPHTTPLIB_MBEDTLS_V4 + // Set random number generator (removed in Mbed TLS 4.x; PSA RNG is used) mbedtls_ssl_conf_rng(&ctx->conf, mbedtls_ctr_drbg_random, &ctx->ctr_drbg); +#endif // Default: verify peer certificate mbedtls_ssl_conf_authmode(&ctx->conf, MBEDTLS_SSL_VERIFY_REQUIRED); @@ -18124,6 +18205,13 @@ inline ctx_t create_server_context() { ctx->is_server = true; +#ifdef CPPHTTPLIB_MBEDTLS_V4 + if (!detail::ensure_mbedtls_psa_crypto()) { + delete ctx; + return nullptr; + } + int ret = 0; +#else // Seed the random number generator const char *pers = "httplib_server"; int ret = mbedtls_ctr_drbg_seed( @@ -18134,6 +18222,7 @@ inline ctx_t create_server_context() { delete ctx; return nullptr; } +#endif // Set up SSL config for server ret = mbedtls_ssl_config_defaults(&ctx->conf, MBEDTLS_SSL_IS_SERVER, @@ -18145,8 +18234,10 @@ inline ctx_t create_server_context() { return nullptr; } - // Set random number generator +#ifndef CPPHTTPLIB_MBEDTLS_V4 + // Set random number generator (removed in Mbed TLS 4.x; PSA RNG is used) mbedtls_ssl_conf_rng(&ctx->conf, mbedtls_ctr_drbg_random, &ctx->ctr_drbg); +#endif // Default: don't verify client mbedtls_ssl_conf_authmode(&ctx->conf, MBEDTLS_SSL_VERIFY_NONE); @@ -18306,7 +18397,7 @@ inline bool set_client_cert_pem(ctx_t ctx, const char *cert, const char *key, password ? reinterpret_cast(password) : nullptr; size_t pwd_len = password ? strlen(password) : 0; -#ifdef CPPHTTPLIB_MBEDTLS_V3 +#if defined(CPPHTTPLIB_MBEDTLS_V3) && !defined(CPPHTTPLIB_MBEDTLS_V4) ret = mbedtls_pk_parse_key( &mctx->own_key, reinterpret_cast(key_str.c_str()), key_str.size() + 1, pwd, pwd_len, mbedtls_ctr_drbg_random, @@ -18321,7 +18412,10 @@ inline bool set_client_cert_pem(ctx_t ctx, const char *cert, const char *key, return false; } - // Verify that the certificate and private key match + // Verify that the certificate and private key match. + // Mbed TLS 4.x: mbedtls_pk_check_pair() returns PSA_ERROR_INVALID_ARGUMENT + // for PSA-backed keys even when they match, so skip and rely on later use. +#ifndef CPPHTTPLIB_MBEDTLS_V4 #ifdef CPPHTTPLIB_MBEDTLS_V3 ret = mbedtls_pk_check_pair(&mctx->own_cert.pk, &mctx->own_key, mbedtls_ctr_drbg_random, &mctx->ctr_drbg); @@ -18332,6 +18426,7 @@ inline bool set_client_cert_pem(ctx_t ctx, const char *cert, const char *key, impl::mbedtls_last_error() = ret; return false; } +#endif ret = mbedtls_ssl_conf_own_cert(&mctx->conf, &mctx->own_cert, &mctx->own_key); if (ret != 0) { @@ -18355,7 +18450,7 @@ inline bool set_client_cert_file(ctx_t ctx, const char *cert_path, } // Parse private key file -#ifdef CPPHTTPLIB_MBEDTLS_V3 +#if defined(CPPHTTPLIB_MBEDTLS_V3) && !defined(CPPHTTPLIB_MBEDTLS_V4) ret = mbedtls_pk_parse_keyfile(&mctx->own_key, key_path, password, mbedtls_ctr_drbg_random, &mctx->ctr_drbg); #else @@ -18366,7 +18461,9 @@ inline bool set_client_cert_file(ctx_t ctx, const char *cert_path, return false; } - // Verify that the certificate and private key match + // Verify that the certificate and private key match. + // Mbed TLS 4.x: see set_client_cert() — skip mbedtls_pk_check_pair(). +#ifndef CPPHTTPLIB_MBEDTLS_V4 #ifdef CPPHTTPLIB_MBEDTLS_V3 ret = mbedtls_pk_check_pair(&mctx->own_cert.pk, &mctx->own_key, mbedtls_ctr_drbg_random, &mctx->ctr_drbg); @@ -18377,6 +18474,7 @@ inline bool set_client_cert_file(ctx_t ctx, const char *cert_path, impl::mbedtls_last_error() = ret; return false; } +#endif ret = mbedtls_ssl_conf_own_cert(&mctx->conf, &mctx->own_cert, &mctx->own_key); if (ret != 0) { @@ -18471,7 +18569,14 @@ inline TlsError connect(session_t session) { } auto msession = static_cast(session); - int ret = mbedtls_ssl_handshake(&msession->ssl); + int ret; + do { + ret = mbedtls_ssl_handshake(&msession->ssl); +#if defined(MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET) + } while (ret == MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET); +#else + } while (false); +#endif if (ret == 0) { err.code = ErrorCode::Success; @@ -18515,6 +18620,11 @@ inline bool connect_nonblocking(session_t session, socket_t sock, int ret; while ((ret = mbedtls_ssl_handshake(&msession->ssl)) != 0) { +#if defined(MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET) + if (ret == MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET) { + continue; // non-fatal TLS 1.3 ticket; retry immediately + } +#endif if (ret == MBEDTLS_ERR_SSL_WANT_READ) { if (detail::select_read(sock, timeout_sec, timeout_usec) > 0) { continue; @@ -18562,8 +18672,16 @@ inline ssize_t read(session_t session, void *buf, size_t len, TlsError &err) { } auto msession = static_cast(session); - int ret = - mbedtls_ssl_read(&msession->ssl, static_cast(buf), len); + int ret; + do { + ret = mbedtls_ssl_read(&msession->ssl, static_cast(buf), + len); +#if defined(MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET) + // TLS 1.3 session tickets are notifications, not application data. + } while (ret == MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET); +#else + } while (false); +#endif if (ret > 0) { err.code = ErrorCode::Success; @@ -18628,10 +18746,7 @@ inline void shutdown(session_t session, bool graceful) { int attempts = 0; while ((ret = mbedtls_ssl_close_notify(&msession->ssl)) != 0 && attempts < 3) { - if (ret != MBEDTLS_ERR_SSL_WANT_READ && - ret != MBEDTLS_ERR_SSL_WANT_WRITE) { - break; - } + if (!impl::mbedtls_should_retry(ret)) { break; } attempts++; } } @@ -18655,7 +18770,14 @@ inline bool is_peer_closed(session_t session, socket_t sock) { // purpose of checking if peer is closed, this should be acceptable // since we're only called when we expect the connection might be closing unsigned char buf; - int ret = mbedtls_ssl_read(&msession->ssl, &buf, 1); + int ret; + do { + ret = mbedtls_ssl_read(&msession->ssl, &buf, 1); +#if defined(MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET) + } while (ret == MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET); +#else + } while (false); +#endif // If we got data or WANT_READ (would block), connection is alive if (ret > 0 || ret == MBEDTLS_ERR_SSL_WANT_READ) { return false; } @@ -19065,7 +19187,7 @@ inline bool update_server_cert(ctx_t ctx, const char *cert_pem, } // Parse private key PEM -#ifdef CPPHTTPLIB_MBEDTLS_V3 +#if defined(CPPHTTPLIB_MBEDTLS_V3) && !defined(CPPHTTPLIB_MBEDTLS_V4) ret = mbedtls_pk_parse_key( &mbed_ctx->own_key, reinterpret_cast(key_pem), strlen(key_pem) + 1, diff --git a/meson.build b/meson.build index 3cd31c243e..760a236e36 100644 --- a/meson.build +++ b/meson.build @@ -51,7 +51,11 @@ if get_option('tls').allowed() else mbedtls_dep = dependency('mbedtls', required: get_option('tls')) mbedtlsx509_dep = dependency('mbedx509', required: get_option('tls')) - mbedtlscrypto_dep = dependency('mbedcrypto', required: get_option('tls')) + # Mbed TLS 4.x renamed libmbedcrypto to libtfpsacrypto + mbedtlscrypto_dep = dependency('tfpsacrypto', required: false) + if not mbedtlscrypto_dep.found() + mbedtlscrypto_dep = dependency('mbedcrypto', required: get_option('tls')) + endif if mbedtls_dep.found() and mbedtlsx509_dep.found() and mbedtlscrypto_dep.found() deps += mbedtls_dep deps += mbedtlsx509_dep diff --git a/test/Makefile b/test/Makefile index 9441e36f89..32f3ce6a1b 100644 --- a/test/Makefile +++ b/test/Makefile @@ -9,14 +9,24 @@ ifneq ($(OS), Windows_NT) OPENSSL_SUPPORT = -DCPPHTTPLIB_OPENSSL_SUPPORT -I$(OPENSSL_DIR)/include -L$(OPENSSL_DIR)/lib -lssl -lcrypto OPENSSL_SUPPORT += -framework CoreFoundation -framework Security MBEDTLS_DIR ?= $(shell brew --prefix mbedtls@3) - MBEDTLS_SUPPORT = -DCPPHTTPLIB_MBEDTLS_SUPPORT -I$(MBEDTLS_DIR)/include -L$(MBEDTLS_DIR)/lib -lmbedtls -lmbedx509 -lmbedcrypto + # Mbed TLS 4.x uses -ltfpsacrypto instead of -lmbedcrypto + MBEDTLS_CRYPTO_LIB ?= $(shell test -f "$(MBEDTLS_DIR)/lib/libtfpsacrypto.dylib" -o -f "$(MBEDTLS_DIR)/lib/libtfpsacrypto.a" && echo tfpsacrypto || echo mbedcrypto) + MBEDTLS_SUPPORT = -DCPPHTTPLIB_MBEDTLS_SUPPORT -I$(MBEDTLS_DIR)/include -L$(MBEDTLS_DIR)/lib -lmbedtls -lmbedx509 -l$(MBEDTLS_CRYPTO_LIB) MBEDTLS_SUPPORT += -framework CoreFoundation -framework Security WOLFSSL_DIR ?= $(shell brew --prefix wolfssl) WOLFSSL_SUPPORT = -DCPPHTTPLIB_WOLFSSL_SUPPORT -I$(WOLFSSL_DIR)/include -I$(WOLFSSL_DIR)/include/wolfssl -L$(WOLFSSL_DIR)/lib -lwolfssl WOLFSSL_SUPPORT += -framework CoreFoundation -framework Security else OPENSSL_SUPPORT = -DCPPHTTPLIB_OPENSSL_SUPPORT -lssl -lcrypto - MBEDTLS_SUPPORT = -DCPPHTTPLIB_MBEDTLS_SUPPORT -lmbedtls -lmbedx509 -lmbedcrypto + # Optional prefix for a custom/built Mbed TLS (e.g. 4.x from source). + # When unset, use system libs; prefer tfpsacrypto (4.x) over mbedcrypto. + ifdef MBEDTLS_DIR + MBEDTLS_CRYPTO_LIB ?= $(shell test -f "$(MBEDTLS_DIR)/lib/libtfpsacrypto.so" -o -f "$(MBEDTLS_DIR)/lib/libtfpsacrypto.a" && echo tfpsacrypto || echo mbedcrypto) + MBEDTLS_SUPPORT = -DCPPHTTPLIB_MBEDTLS_SUPPORT -I$(MBEDTLS_DIR)/include -L$(MBEDTLS_DIR)/lib -Wl,-rpath,$(MBEDTLS_DIR)/lib -lmbedtls -lmbedx509 -l$(MBEDTLS_CRYPTO_LIB) + else + MBEDTLS_CRYPTO_LIB ?= $(shell (ldconfig -p 2>/dev/null | grep -q libtfpsacrypto && echo tfpsacrypto) || (test -f /usr/lib/libtfpsacrypto.so -o -f /usr/lib/x86_64-linux-gnu/libtfpsacrypto.so -o -f /usr/lib/aarch64-linux-gnu/libtfpsacrypto.so && echo tfpsacrypto) || echo mbedcrypto) + MBEDTLS_SUPPORT = -DCPPHTTPLIB_MBEDTLS_SUPPORT -lmbedtls -lmbedx509 -l$(MBEDTLS_CRYPTO_LIB) + endif WOLFSSL_SUPPORT = -DCPPHTTPLIB_WOLFSSL_SUPPORT -lwolfssl ifeq ($(UNAME_S), Linux) # Disable ASLR for ASAN compatibility on WSL2 (high-entropy ASLR conflicts with ASAN shadow memory) diff --git a/test/test.cc b/test/test.cc index b1d4847622..be48b11527 100644 --- a/test/test.cc +++ b/test/test.cc @@ -18333,8 +18333,9 @@ TEST(SSLClientServerTest, CustomizeServerSSLCtxMbedTLS) { return false; } // Load server private key + // Mbed TLS 3.x takes an RNG callback; 2.x and 4.x do not. if (mbedtls_pk_parse_keyfile(&own_key, SERVER_PRIVATE_KEY_FILE, nullptr -#if MBEDTLS_VERSION_MAJOR >= 3 +#if MBEDTLS_VERSION_MAJOR == 3 , mbedtls_ctr_drbg_random, nullptr #endif From 97c43261183dd3670068959e35e4005afc1f1fd7 Mon Sep 17 00:00:00 2001 From: Dave Johansen Date: Fri, 17 Jul 2026 17:05:34 -0600 Subject: [PATCH 2/4] Fix Mbed TLS 4 CI: PBES2 test keys and clang-format Mbed TLS 4 rejects PBE-SHA1-3DES encrypted client keys used by gen-certs, so generate PBES2 AES-wrapped keys instead. Also satisfy clang-format 18. Co-authored-by: Cursor --- httplib.h | 33 ++++++++++++++++----------------- test/CMakeLists.txt | 13 ++++++++++--- test/gen-certs.sh | 9 +++++++-- test/meson.build | 20 ++++++++++++++------ 4 files changed, 47 insertions(+), 28 deletions(-) diff --git a/httplib.h b/httplib.h index 6430579c9d..75e44bf67a 100644 --- a/httplib.h +++ b/httplib.h @@ -9095,8 +9095,7 @@ inline bool psa_hash_to_buf(psa_algorithm_t alg, const std::string &s, if (!ensure_mbedtls_psa_crypto()) { return false; } size_t out_len = 0; return psa_hash_compute(alg, reinterpret_cast(s.data()), - s.size(), hash, hash_size, - &out_len) == PSA_SUCCESS && + s.size(), hash, hash_size, &out_len) == PSA_SUCCESS && out_len == hash_size; } #endif @@ -19553,23 +19552,23 @@ inline bool load_system_certs(ctx_t ctx) { SSL_FILETYPE_ASN1) == SSL_SUCCESS; }); #else - for (auto path = impl::system_ca_paths(); *path; ++path) { - if (wolfSSL_CTX_load_verify_locations(wctx->ctx, *path, nullptr) == - SSL_SUCCESS) { - loaded = true; - break; - } - } + for (auto path = impl::system_ca_paths(); *path; ++path) { + if (wolfSSL_CTX_load_verify_locations(wctx->ctx, *path, nullptr) == + SSL_SUCCESS) { + loaded = true; + break; + } + } - if (!loaded) { - for (auto dir = impl::system_ca_dirs(); *dir; ++dir) { - if (wolfSSL_CTX_load_verify_locations(wctx->ctx, nullptr, *dir) == - SSL_SUCCESS) { - loaded = true; - break; + if (!loaded) { + for (auto dir = impl::system_ca_dirs(); *dir; ++dir) { + if (wolfSSL_CTX_load_verify_locations(wctx->ctx, nullptr, *dir) == + SSL_SUCCESS) { + loaded = true; + break; + } + } } - } - } #endif return loaded; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index c1c72f03f1..25ec1d0d18 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -103,19 +103,26 @@ if(HTTPLIB_IS_USING_OPENSSL) WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} COMMAND_ERROR_IS_FATAL ANY ) + # Unencrypted key -> cert, then PBES2 AES wrap (Mbed TLS 4.x has no DES/PBES1). execute_process( - COMMAND ${OPENSSL_COMMAND} genrsa -aes256 -passout pass:test012! 2048 - OUTPUT_FILE client_encrypted.key.pem + COMMAND ${OPENSSL_COMMAND} genrsa 2048 + OUTPUT_FILE client_encrypted.tmp.key.pem WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} COMMAND_ERROR_IS_FATAL ANY ) execute_process( - COMMAND ${OPENSSL_COMMAND} req -new -batch -config ${CMAKE_CURRENT_LIST_DIR}/test.conf -key client_encrypted.key.pem -passin pass:test012! + COMMAND ${OPENSSL_COMMAND} req -new -batch -config ${CMAKE_CURRENT_LIST_DIR}/test.conf -key client_encrypted.tmp.key.pem COMMAND ${OPENSSL_COMMAND} x509 -days 370 -req -CA rootCA.cert.pem -CAkey rootCA.key.pem -CAcreateserial OUTPUT_FILE client_encrypted.cert.pem WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} COMMAND_ERROR_IS_FATAL ANY ) + execute_process( + COMMAND ${OPENSSL_COMMAND} pkcs8 -topk8 -v2 aes-256-cbc -in client_encrypted.tmp.key.pem -passout pass:test012! -out client_encrypted.key.pem + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMAND_ERROR_IS_FATAL ANY + ) + file(REMOVE ${CMAKE_CURRENT_BINARY_DIR}/client_encrypted.tmp.key.pem) # Certificates for IP-host hostname verification regression tests. # cert_ip_cn.pem: CN is an IPv4 literal with NO subjectAltName. An IP host # must NOT be authenticated via the CN, so verifying it diff --git a/test/gen-certs.sh b/test/gen-certs.sh index b25c2f0bcf..f464989562 100755 --- a/test/gen-certs.sh +++ b/test/gen-certs.sh @@ -14,8 +14,13 @@ openssl genrsa 2048 > client.key.pem openssl req -new -batch -config test.conf -key client.key.pem | openssl x509 -days 370 -req -CA rootCA.cert.pem -CAkey rootCA.key.pem -CAcreateserial > client.cert.pem openssl genrsa -passout pass:test123! 2048 > key_encrypted.pem openssl req -new -batch -config test.conf -key key_encrypted.pem | openssl x509 -days 3650 -req -signkey key_encrypted.pem > cert_encrypted.pem -openssl genrsa 2048 | openssl pkcs8 -topk8 -v1 PBE-SHA1-3DES -passout pass:test012! -out client_encrypted.key.pem -openssl req -new -batch -config test.conf -key client_encrypted.key.pem -passin pass:test012! | openssl x509 -days 370 -req -CA rootCA.cert.pem -CAkey rootCA.key.pem -CAcreateserial > client_encrypted.cert.pem +# Generate an unencrypted key first so openssl req does not need a passphrase +# (broken for PKCS#8 on some OpenSSL 3.x builds), then wrap with PBES2 AES. +# PBE-SHA1-3DES is rejected by Mbed TLS 4.x (DES removed from default builds). +openssl genrsa 2048 > client_encrypted.tmp.key.pem +openssl req -new -batch -config test.conf -key client_encrypted.tmp.key.pem | openssl x509 -days 370 -req -CA rootCA.cert.pem -CAkey rootCA.key.pem -CAcreateserial > client_encrypted.cert.pem +openssl pkcs8 -topk8 -v2 aes-256-cbc -in client_encrypted.tmp.key.pem -passout pass:test012! -out client_encrypted.key.pem +rm -f client_encrypted.tmp.key.pem # Certificates for IP-host hostname verification regression tests. # cert_ip_cn.pem: CN is an IPv4 literal with NO subjectAltName. An IP host must diff --git a/test/meson.build b/test/meson.build index 745236b9e3..f86d07e57a 100644 --- a/test/meson.build +++ b/test/meson.build @@ -81,17 +81,18 @@ client_cert_pem = custom_target( command: [openssl, 'x509', '-in', '@INPUT0@', '-days', '370', '-req', '-CA', '@INPUT1@', '-CAkey', '@INPUT2@', '-CAcreateserial', '-out', '@OUTPUT@'] ) -client_encrypted_key_pem = custom_target( - 'client_encrypted_key_pem', - output: 'client_encrypted.key.pem', - command: [openssl, 'genrsa', '-aes256', '-passout', 'pass:test012!', '-out', '@OUTPUT@', '2048'] +# Unencrypted key -> cert, then PBES2 AES wrap (Mbed TLS 4.x has no DES/PBES1). +client_encrypted_tmp_key_pem = custom_target( + 'client_encrypted_tmp_key_pem', + output: 'client_encrypted.tmp.key.pem', + command: [openssl, 'genrsa', '-out', '@OUTPUT@', '2048'] ) client_encrypted_temp_req = custom_target( 'client_encrypted_temp_req', - input: client_encrypted_key_pem, + input: client_encrypted_tmp_key_pem, output: 'client_encrypted_temp_req', - command: [openssl, 'req', '-new', '-batch', '-config', test_conf, '-key', '@INPUT@', '-passin', 'pass:test012!', '-out', '@OUTPUT@'] + command: [openssl, 'req', '-new', '-batch', '-config', test_conf, '-key', '@INPUT@', '-out', '@OUTPUT@'] ) client_encrypted_cert_pem = custom_target( @@ -101,6 +102,13 @@ client_encrypted_cert_pem = custom_target( command: [openssl, 'x509', '-in', '@INPUT0@', '-days', '370', '-req', '-CA', '@INPUT1@', '-CAkey', '@INPUT2@', '-CAcreateserial', '-out', '@OUTPUT@'] ) +client_encrypted_key_pem = custom_target( + 'client_encrypted_key_pem', + input: client_encrypted_tmp_key_pem, + output: 'client_encrypted.key.pem', + command: [openssl, 'pkcs8', '-topk8', '-v2', 'aes-256-cbc', '-in', '@INPUT@', '-passout', 'pass:test012!', '-out', '@OUTPUT@'] +) + # Copy test files to the build directory configure_file(input: 'ca-bundle.crt', output: 'ca-bundle.crt', copy: true) configure_file(input: 'image.jpg', output: 'image.jpg', copy: true) From 887c0d5b498533fd0f36e60354477c26e0ca3a40 Mon Sep 17 00:00:00 2001 From: Dave Johansen Date: Fri, 17 Jul 2026 17:41:27 -0600 Subject: [PATCH 3/4] Support both PBES1 and PBES2 encrypted client test keys Ubuntu's Mbed TLS 2.28 can only decrypt DES/PBES1 keys, while 4.x rejects DES. Generate both wraps from one keypair and select by Mbed TLS version. Co-authored-by: Cursor --- test/CMakeLists.txt | 5 +++++ test/gen-certs.sh | 7 +++++-- test/meson.build | 8 ++++++++ test/test.cc | 7 +++++++ 4 files changed, 25 insertions(+), 2 deletions(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 25ec1d0d18..9e7031cdf6 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -122,6 +122,11 @@ if(HTTPLIB_IS_USING_OPENSSL) WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} COMMAND_ERROR_IS_FATAL ANY ) + execute_process( + COMMAND ${OPENSSL_COMMAND} pkcs8 -topk8 -v1 PBE-SHA1-3DES -in client_encrypted.tmp.key.pem -passout pass:test012! -out client_encrypted_pbes1.key.pem + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMAND_ERROR_IS_FATAL ANY + ) file(REMOVE ${CMAKE_CURRENT_BINARY_DIR}/client_encrypted.tmp.key.pem) # Certificates for IP-host hostname verification regression tests. # cert_ip_cn.pem: CN is an IPv4 literal with NO subjectAltName. An IP host diff --git a/test/gen-certs.sh b/test/gen-certs.sh index f464989562..c1c302fedb 100755 --- a/test/gen-certs.sh +++ b/test/gen-certs.sh @@ -15,11 +15,14 @@ openssl req -new -batch -config test.conf -key client.key.pem | openssl x509 -da openssl genrsa -passout pass:test123! 2048 > key_encrypted.pem openssl req -new -batch -config test.conf -key key_encrypted.pem | openssl x509 -days 3650 -req -signkey key_encrypted.pem > cert_encrypted.pem # Generate an unencrypted key first so openssl req does not need a passphrase -# (broken for PKCS#8 on some OpenSSL 3.x builds), then wrap with PBES2 AES. -# PBE-SHA1-3DES is rejected by Mbed TLS 4.x (DES removed from default builds). +# (broken for PKCS#8 on some OpenSSL 3.x builds), then wrap twice from the same +# keypair: PBES2 AES for Mbed TLS 3.6+/4.x and OpenSSL/wolfSSL, and PBES1 +# 3DES for older Mbed TLS (e.g. Ubuntu 2.28) which has no PKCS5-AES support. +# Mbed TLS 4.x rejects DES/PBES1. openssl genrsa 2048 > client_encrypted.tmp.key.pem openssl req -new -batch -config test.conf -key client_encrypted.tmp.key.pem | openssl x509 -days 370 -req -CA rootCA.cert.pem -CAkey rootCA.key.pem -CAcreateserial > client_encrypted.cert.pem openssl pkcs8 -topk8 -v2 aes-256-cbc -in client_encrypted.tmp.key.pem -passout pass:test012! -out client_encrypted.key.pem +openssl pkcs8 -topk8 -v1 PBE-SHA1-3DES -in client_encrypted.tmp.key.pem -passout pass:test012! -out client_encrypted_pbes1.key.pem rm -f client_encrypted.tmp.key.pem # Certificates for IP-host hostname verification regression tests. diff --git a/test/meson.build b/test/meson.build index f86d07e57a..7d3f255735 100644 --- a/test/meson.build +++ b/test/meson.build @@ -109,6 +109,13 @@ client_encrypted_key_pem = custom_target( command: [openssl, 'pkcs8', '-topk8', '-v2', 'aes-256-cbc', '-in', '@INPUT@', '-passout', 'pass:test012!', '-out', '@OUTPUT@'] ) +client_encrypted_pbes1_key_pem = custom_target( + 'client_encrypted_pbes1_key_pem', + input: client_encrypted_tmp_key_pem, + output: 'client_encrypted_pbes1.key.pem', + command: [openssl, 'pkcs8', '-topk8', '-v1', 'PBE-SHA1-3DES', '-in', '@INPUT@', '-passout', 'pass:test012!', '-out', '@OUTPUT@'] +) + # Copy test files to the build directory configure_file(input: 'ca-bundle.crt', output: 'ca-bundle.crt', copy: true) configure_file(input: 'image.jpg', output: 'image.jpg', copy: true) @@ -147,6 +154,7 @@ test( client_key_pem, client_cert_pem, client_encrypted_key_pem, + client_encrypted_pbes1_key_pem, client_encrypted_cert_pem ], workdir: meson.current_build_dir(), diff --git a/test/test.cc b/test/test.cc index be48b11527..f1c4c8423c 100644 --- a/test/test.cc +++ b/test/test.cc @@ -47,7 +47,14 @@ inline std::string u8_to_string(const char8_t *s) { #define CLIENT_CERT_FILE "./client.cert.pem" #define CLIENT_PRIVATE_KEY_FILE "./client.key.pem" #define CLIENT_ENCRYPTED_CERT_FILE "./client_encrypted.cert.pem" +// Mbed TLS < 3.6 has no PKCS5 PBES2-AES; Ubuntu's 2.28 package needs PBES1. +// Mbed TLS 4.x drops DES, so newer builds use the AES-wrapped key instead. +#if defined(CPPHTTPLIB_MBEDTLS_SUPPORT) && \ + (MBEDTLS_VERSION_NUMBER < 0x03060000) +#define CLIENT_ENCRYPTED_PRIVATE_KEY_FILE "./client_encrypted_pbes1.key.pem" +#else #define CLIENT_ENCRYPTED_PRIVATE_KEY_FILE "./client_encrypted.key.pem" +#endif #define CLIENT_ENCRYPTED_PRIVATE_KEY_PASS "test012!" #define SERVER_ENCRYPTED_CERT_FILE "./cert_encrypted.pem" #define SERVER_ENCRYPTED_PRIVATE_KEY_FILE "./key_encrypted.pem" From 63730b316f18bd34331fb07904b9bc2cc1fde1ff Mon Sep 17 00:00:00 2001 From: Dave Johansen Date: Sat, 18 Jul 2026 11:49:21 -0600 Subject: [PATCH 4/4] Fix clang-format on encrypted client key version gate Co-authored-by: Cursor --- test/test.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/test.cc b/test/test.cc index f1c4c8423c..cdef969f33 100644 --- a/test/test.cc +++ b/test/test.cc @@ -49,8 +49,7 @@ inline std::string u8_to_string(const char8_t *s) { #define CLIENT_ENCRYPTED_CERT_FILE "./client_encrypted.cert.pem" // Mbed TLS < 3.6 has no PKCS5 PBES2-AES; Ubuntu's 2.28 package needs PBES1. // Mbed TLS 4.x drops DES, so newer builds use the AES-wrapped key instead. -#if defined(CPPHTTPLIB_MBEDTLS_SUPPORT) && \ - (MBEDTLS_VERSION_NUMBER < 0x03060000) +#if defined(CPPHTTPLIB_MBEDTLS_SUPPORT) && (MBEDTLS_VERSION_NUMBER < 0x03060000) #define CLIENT_ENCRYPTED_PRIVATE_KEY_FILE "./client_encrypted_pbes1.key.pem" #else #define CLIENT_ENCRYPTED_PRIVATE_KEY_FILE "./client_encrypted.key.pem"