diff --git a/Kconfig b/Kconfig index 6ed57da043..4a0abb9534 100644 --- a/Kconfig +++ b/Kconfig @@ -743,3 +743,4 @@ mainmenu "Espressif IoT Development Framework Configuration" - CONFIG_LIBC_PICOLIBC - CONFIG_GDMA_ENABLE_WEIGHTED_ARBITRATION - CONFIG_I3C_MASTER_ENABLED + - CONFIG_MBEDTLS_ESP_IDF_USE_PSA_CRYPTO diff --git a/components/esp_http_client/CMakeLists.txt b/components/esp_http_client/CMakeLists.txt index 4a353e33ca..fb93315879 100644 --- a/components/esp_http_client/CMakeLists.txt +++ b/components/esp_http_client/CMakeLists.txt @@ -4,10 +4,17 @@ else() set(req linux esp_event) endif() +if(CONFIG_ESP_HTTP_CLIENT_PSA_CRYPTO_MIGRATE) + set(HTTP_CRYPTO_SRC "lib/http_crypto_psa.c") +else() + set(HTTP_CRYPTO_SRC "lib/http_crypto_mbedtls.c") +endif() + idf_component_register(SRCS "esp_http_client.c" "lib/http_auth.c" "lib/http_header.c" "lib/http_utils.c" + ${HTTP_CRYPTO_SRC} INCLUDE_DIRS "include" PRIV_INCLUDE_DIRS "lib/include" # lwip is a public requirement because esp_http_client.h includes sys/socket.h diff --git a/components/esp_http_client/Kconfig b/components/esp_http_client/Kconfig index 947a5ab8db..5435a86aed 100644 --- a/components/esp_http_client/Kconfig +++ b/components/esp_http_client/Kconfig @@ -41,4 +41,11 @@ menu "ESP HTTP client" help This config option helps in setting the time in millisecond to wait for event to be posted to the system default event loop. Set it to -1 if you need to set timeout to portMAX_DELAY. + + config ESP_HTTP_CLIENT_PSA_CRYPTO_MIGRATE + depends on MBEDTLS_ESP_IDF_USE_PSA_CRYPTO + bool "Migrate ESP HTTP Client to use PSA Crypto" + default y + help + Migrate ESP HTTP Client to use PSA Crypto. endmenu diff --git a/components/esp_http_client/lib/http_auth.c b/components/esp_http_client/lib/http_auth.c index 0bbe054f66..7346ed5756 100644 --- a/components/esp_http_client/lib/http_auth.c +++ b/components/esp_http_client/lib/http_auth.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -12,17 +12,15 @@ #include "sys/socket.h" #include "esp_rom_md5.h" #include "esp_tls_crypto.h" -#include "mbedtls/sha256.h" #include "esp_log.h" #include "esp_check.h" #include "http_utils.h" #include "http_auth.h" +#include "http_crypto.h" #define MD5_MAX_LEN (33) -#define SHA256_LEN (32) -#define SHA256_HEX_LEN (65) #define HTTP_AUTH_BUF_LEN (1024) static const char *TAG = "HTTP_AUTH"; @@ -85,19 +83,13 @@ static int sha256_sprintf(char *sha, const char *fmt, ...) } int ret = 0; - mbedtls_sha256_context sha256; - mbedtls_sha256_init(&sha256); - if (mbedtls_sha256_starts(&sha256, 0) != 0) { - goto exit; - } - if (mbedtls_sha256_update(&sha256, buf, len) != 0) { - goto exit; - } - if (mbedtls_sha256_finish(&sha256, digest) != 0) { + + esp_err_t err = http_crypto_sha256(buf, len, digest); + if (err != ESP_OK) { goto exit; } - for (i = 0; i < 32; ++i) { + for (i = 0; i < SHA256_LEN; ++i) { sprintf(&sha[i * 2], "%02x", (unsigned int)digest[i]); } sha[SHA256_HEX_LEN - 1] = '\0'; @@ -105,7 +97,6 @@ static int sha256_sprintf(char *sha, const char *fmt, ...) exit: free(buf); - mbedtls_sha256_free(&sha256); va_end(ap); return ret; } diff --git a/components/esp_http_client/lib/http_crypto_mbedtls.c b/components/esp_http_client/lib/http_crypto_mbedtls.c new file mode 100644 index 0000000000..62f730fafa --- /dev/null +++ b/components/esp_http_client/lib/http_crypto_mbedtls.c @@ -0,0 +1,47 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "http_crypto.h" +#include "esp_rom_md5.h" +#include "esp_err.h" +#include "mbedtls/sha256.h" +#include "esp_log.h" + +static const char *TAG = "http_crypto_mbedtls"; + + +esp_err_t http_crypto_sha256(const uint8_t *data, size_t data_len, uint8_t *hash) +{ + if (data == NULL || data_len == 0 || hash == NULL) { + ESP_LOGE(TAG, "Invalid input parameters"); + return ESP_FAIL; + } + + esp_err_t err = ESP_FAIL; + mbedtls_sha256_context ctx; + mbedtls_sha256_init(&ctx); + + if (mbedtls_sha256_starts(&ctx, false) != 0) { + ESP_LOGE(TAG, "Failed to start SHA-256 hash"); + goto exit; + } + + if (mbedtls_sha256_update(&ctx, data, data_len) != 0) { + ESP_LOGE(TAG, "Failed to update SHA-256 hash"); + goto exit; + } + + if (mbedtls_sha256_finish(&ctx, hash) != 0) { + ESP_LOGE(TAG, "Failed to finish SHA-256 hash"); + goto exit; + } + + err = ESP_OK; + +exit: + mbedtls_sha256_free(&ctx); + return err; +} diff --git a/components/esp_http_client/lib/http_crypto_psa.c b/components/esp_http_client/lib/http_crypto_psa.c new file mode 100644 index 0000000000..8866b2694c --- /dev/null +++ b/components/esp_http_client/lib/http_crypto_psa.c @@ -0,0 +1,30 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include +#include "esp_err.h" +#include "http_crypto.h" +#include "psa/crypto.h" +#include "esp_log.h" + +static const char *TAG = "http_crypto_psa"; + +esp_err_t http_crypto_sha256(const uint8_t *data, size_t data_len, uint8_t *hash) +{ + if (data == NULL || data_len == 0 || hash == NULL) { + ESP_LOGE(TAG, "Invalid input parameters"); + return ESP_FAIL; + } + + size_t hash_len = 0; + psa_status_t status = psa_hash_compute(PSA_ALG_SHA_256, data, data_len, hash, SHA256_LEN, &hash_len); + if (status != PSA_SUCCESS || hash_len != SHA256_LEN) { + ESP_LOGE(TAG, "Failed to compute SHA-256 hash"); + return ESP_FAIL; + } + return ESP_OK; +} diff --git a/components/esp_http_client/lib/include/http_crypto.h b/components/esp_http_client/lib/include/http_crypto.h new file mode 100644 index 0000000000..53edfa0468 --- /dev/null +++ b/components/esp_http_client/lib/include/http_crypto.h @@ -0,0 +1,25 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include "esp_err.h" + + +#define SHA256_LEN (32) +#define SHA256_HEX_LEN (65) + +esp_err_t http_crypto_sha256(const uint8_t *data, size_t data_len, uint8_t *hash); + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_http_server/CMakeLists.txt b/components/esp_http_server/CMakeLists.txt index c33f446138..df86efa977 100644 --- a/components/esp_http_server/CMakeLists.txt +++ b/components/esp_http_server/CMakeLists.txt @@ -2,12 +2,19 @@ set(priv_req mbedtls lwip esp_timer) set(priv_inc_dir "src/util" "src/port/esp32") set(requires http_parser esp_event) +if(CONFIG_HTTPD_SERVER_PSA_CRYPTO_MIGRATE) + set(HTTPD_CRYPTO_SRC "src/httpd_crypto_psa.c") +else() + set(HTTPD_CRYPTO_SRC "src/httpd_crypto_mbedtls.c") +endif() + idf_component_register(SRCS "src/httpd_main.c" "src/httpd_parse.c" "src/httpd_sess.c" "src/httpd_txrx.c" "src/httpd_uri.c" "src/httpd_ws.c" + ${HTTPD_CRYPTO_SRC} "src/util/ctrl_sock.c" INCLUDE_DIRS "include" PRIV_INCLUDE_DIRS ${priv_inc_dir} diff --git a/components/esp_http_server/Kconfig b/components/esp_http_server/Kconfig index 5078d70da6..a08183c2bb 100644 --- a/components/esp_http_server/Kconfig +++ b/components/esp_http_server/Kconfig @@ -72,4 +72,11 @@ menu "HTTP Server" Enable this option to use WebSocket pre-handshake callback. This will allow the server to register a callback function that will be called before the WebSocket handshake is processed i.e. before switching to the WebSocket protocol. + + config HTTPD_SERVER_PSA_CRYPTO_MIGRATE + depends on MBEDTLS_ESP_IDF_USE_PSA_CRYPTO + bool "Migrate ESP HTTP Server to use PSA Crypto" + default y + help + Migrate ESP HTTP Server to use PSA Crypto. endmenu diff --git a/components/esp_http_server/src/esp_httpd_priv.h b/components/esp_http_server/src/esp_httpd_priv.h index 334642da65..7816bfe417 100644 --- a/components/esp_http_server/src/esp_httpd_priv.h +++ b/components/esp_http_server/src/esp_httpd_priv.h @@ -593,6 +593,8 @@ esp_err_t httpd_sess_trigger_close_(httpd_handle_t handle, struct sock_db *sessi */ void esp_http_server_dispatch_event(int32_t event_id, const void* event_data, size_t event_data_size); +esp_err_t httpd_crypto_sha1(const uint8_t *data, size_t data_len, uint8_t *hash); + #ifdef __cplusplus } #endif diff --git a/components/esp_http_server/src/httpd_crypto_mbedtls.c b/components/esp_http_server/src/httpd_crypto_mbedtls.c new file mode 100644 index 0000000000..fc99158bf3 --- /dev/null +++ b/components/esp_http_server/src/httpd_crypto_mbedtls.c @@ -0,0 +1,45 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "mbedtls/sha1.h" +#include "esp_err.h" +#include "esp_log.h" + +static const char *TAG = "httpd_crypto_mbedtls"; + +#define SHA1_LEN (20) + +esp_err_t httpd_crypto_sha1(const uint8_t *data, size_t data_len, uint8_t *hash) +{ + if (data == NULL || data_len == 0 || hash == NULL) { + ESP_LOGE(TAG, "Invalid input parameters"); + return ESP_FAIL; + } + + esp_err_t err = ESP_FAIL; + mbedtls_sha1_context ctx; + mbedtls_sha1_init(&ctx); + + if (mbedtls_sha1_starts(&ctx) != 0) { + ESP_LOGE(TAG, "Failed to start SHA-1 hash"); + goto exit; + } + + if (mbedtls_sha1_update(&ctx, data, data_len) != 0) { + ESP_LOGE(TAG, "Failed to update SHA-1 hash"); + goto exit; + } + if (mbedtls_sha1_finish(&ctx, hash) != 0) { + ESP_LOGE(TAG, "Failed to finish SHA-1 hash"); + goto exit; + } + + err = ESP_OK; + +exit: + mbedtls_sha1_free(&ctx); + return err; +} diff --git a/components/esp_http_server/src/httpd_crypto_psa.c b/components/esp_http_server/src/httpd_crypto_psa.c new file mode 100644 index 0000000000..a0211fd5d4 --- /dev/null +++ b/components/esp_http_server/src/httpd_crypto_psa.c @@ -0,0 +1,29 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "psa/crypto.h" +#include "esp_err.h" +#include "esp_log.h" + +static const char *TAG = "httpd_crypto_psa"; + +#define SHA1_LEN (20) + +esp_err_t httpd_crypto_sha1(const uint8_t *data, size_t data_len, uint8_t *hash) +{ + if (data == NULL || data_len == 0 || hash == NULL) { + ESP_LOGE(TAG, "Invalid input parameters"); + return ESP_FAIL; + } + + size_t hash_len = 0; + psa_status_t status = psa_hash_compute(PSA_ALG_SHA_1, data, data_len, hash, SHA1_LEN, &hash_len); + if (status != PSA_SUCCESS || hash_len != SHA1_LEN) { + ESP_LOGE(TAG, "Failed to compute SHA-1 hash"); + return ESP_FAIL; + } + return ESP_OK; +} diff --git a/components/esp_http_server/src/httpd_ws.c b/components/esp_http_server/src/httpd_ws.c index 4bffe53842..dff5572a82 100644 --- a/components/esp_http_server/src/httpd_ws.c +++ b/components/esp_http_server/src/httpd_ws.c @@ -11,7 +11,7 @@ #include #include #include -#include + #include #include @@ -143,35 +143,15 @@ esp_err_t httpd_ws_respond_server_handshake(httpd_req_t *req, const char *suppor ESP_LOGD(TAG, LOG_FMT("Server key before encoding: %s"), server_raw_text); - /* Generate SHA-1 first and then encode to Base64 */ - size_t key_len = strlen(server_raw_text); - #if CONFIG_MBEDTLS_SHA1_C || CONFIG_MBEDTLS_HARDWARE_SHA - int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; - mbedtls_sha1_context ctx; - mbedtls_sha1_init(&ctx); - - if ((ret = mbedtls_sha1_starts(&ctx)) != 0) { - goto sha_end; - } - - if ((ret = mbedtls_sha1_update(&ctx, (uint8_t *)server_raw_text, key_len)) != 0) { - goto sha_end; - } - - if ((ret = mbedtls_sha1_finish(&ctx, server_key_hash)) != 0) { - goto sha_end; - } - -sha_end: - mbedtls_sha1_free(&ctx); - if (ret != 0) { - ESP_LOGE(TAG, "Error in calculating SHA1 sum , returned 0x%02X", ret); - return ESP_FAIL; + esp_err_t err = httpd_crypto_sha1((const uint8_t *)server_raw_text, strlen(server_raw_text), server_key_hash); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to compute SHA-1 hash"); + return err; } #else ESP_LOGE(TAG, "Please enable CONFIG_MBEDTLS_SHA1_C or CONFIG_MBEDTLS_HARDWARE_SHA to support SHA1 operations"); - return ESP_FAIL; + return ESP_ERR_NOT_SUPPORTED; #endif /* CONFIG_MBEDTLS_SHA1_C || CONFIG_MBEDTLS_HARDWARE_SHA */ size_t encoded_len = 0; diff --git a/components/esp_system/system_init_fn.txt b/components/esp_system/system_init_fn.txt index fd665d90d4..6a228e2c10 100644 --- a/components/esp_system/system_init_fn.txt +++ b/components/esp_system/system_init_fn.txt @@ -79,6 +79,9 @@ SECONDARY: 102: init_rng in components/esp_hw_support/hw_random.c on BIT(0) # Security specific initializations SECONDARY: 103: esp_security_init in components/esp_security/src/init.c on BIT(0) +# PSA Crypto initialization (must happen after esp_security_init for hardware crypto support) +SECONDARY: 104: mbedtls_psa_crypto_init_fn in components/mbedtls/port/esp_psa_crypto_init.c on BIT(0) + # esp_sleep doesn't have init dependencies SECONDARY: 105: esp_sleep_startup_init in components/esp_hw_support/sleep_gpio.c on BIT(0) SECONDARY: 106: sleep_clock_startup_init in components/esp_hw_support/lowpower/port/esp32c5/sleep_clock.c on BIT(0) diff --git a/components/mbedtls/CMakeLists.txt b/components/mbedtls/CMakeLists.txt index b8f3f1d41d..559a79431d 100644 --- a/components/mbedtls/CMakeLists.txt +++ b/components/mbedtls/CMakeLists.txt @@ -167,6 +167,10 @@ list(APPEND mbedtls_targets everest p256m) set(mbedtls_target_sources "${COMPONENT_DIR}/port/mbedtls_debug.c" "${COMPONENT_DIR}/port/esp_platform_time.c") +if(CONFIG_MBEDTLS_ESP_IDF_USE_PSA_CRYPTO) + list(APPEND mbedtls_target_sources "${COMPONENT_DIR}/port/esp_psa_crypto_init.c") +endif() + if(CONFIG_MBEDTLS_DYNAMIC_BUFFER) set(mbedtls_target_sources ${mbedtls_target_sources} "${COMPONENT_DIR}/port/dynamic/esp_mbedtls_dynamic_impl.c" @@ -357,14 +361,14 @@ foreach(target ${mbedtls_targets}) endif() if(CONFIG_MBEDTLS_COMPILER_OPTIMIZATION_SIZE) target_compile_options(${target} PRIVATE "-Os") - elseif(CONFIG_MBEDTLS_COMPILER_OPTIMIZATION_SPEED) + elseif(CONFIG_MBEDTLS_COMPILER_OPTIMIZATION_PERF) target_compile_options(${target} PRIVATE "-O2") endif() endforeach() if(CONFIG_MBEDTLS_COMPILER_OPTIMIZATION_SIZE) target_compile_options(${COMPONENT_LIB} PRIVATE "-Os") -elseif(CONFIG_MBEDTLS_COMPILER_OPTIMIZATION_SPEED) +elseif(CONFIG_MBEDTLS_COMPILER_OPTIMIZATION_PERF) target_compile_options(${COMPONENT_LIB} PRIVATE "-O2") endif() @@ -398,6 +402,9 @@ endif() target_link_libraries(${COMPONENT_LIB} ${linkage_type} ${mbedtls_targets}) +# Ensure PSA crypto initialization is included in the build +target_link_libraries(${COMPONENT_LIB} ${linkage_type} "-u mbedtls_psa_crypto_init_include_impl") + if(CONFIG_ESP_TLS_USE_DS_PERIPHERAL) # The linker seems to be unable to resolve all the dependencies without increasing this set_property(TARGET mbedcrypto APPEND PROPERTY LINK_INTERFACE_MULTIPLICITY 6) diff --git a/components/mbedtls/Kconfig b/components/mbedtls/Kconfig index 28295bf7e2..aef07fa96a 100644 --- a/components/mbedtls/Kconfig +++ b/components/mbedtls/Kconfig @@ -1,6 +1,15 @@ menu "mbedTLS" menu "Core Configuration" + + config MBEDTLS_ESP_IDF_USE_PSA_CRYPTO + depends on IDF_EXPERIMENTAL_FEATURES + bool "Enable the Platform Security Architecture (PSA) cryptography API for ESP-IDF" + default y + help + Enable the Platform Security Architecture (PSA) cryptography API for ESP-IDF. + This option migrates from mbedtls API to PSA Crypto API. This increases code size. + choice MBEDTLS_COMPILER_OPTIMIZATION prompt "Compiler optimization level" default MBEDTLS_COMPILER_OPTIMIZATION_NONE diff --git a/components/mbedtls/port/esp_hardware.c b/components/mbedtls/port/esp_hardware.c index 5633cccf3a..a89144ff56 100644 --- a/components/mbedtls/port/esp_hardware.c +++ b/components/mbedtls/port/esp_hardware.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -10,6 +10,9 @@ #include #include "esp_random.h" #include "mbedtls/esp_mbedtls_random.h" +#if defined(MBEDTLS_PLATFORM_GET_ENTROPY_ALT) +#include "psa/crypto.h" +#endif #include @@ -31,3 +34,33 @@ int mbedtls_esp_random(void *ctx, unsigned char *buf, size_t len) esp_fill_random(buf, len); return 0; } + +#if defined(MBEDTLS_PLATFORM_GET_ENTROPY_ALT) +int mbedtls_platform_get_entropy(unsigned char *output, size_t output_size, + size_t *output_len, size_t *entropy_content); + +psa_status_t mbedtls_psa_external_get_random( + mbedtls_psa_external_random_context_t *context, + uint8_t *output, size_t output_size, size_t *output_length) +{ + if (context == NULL || output == NULL || output_length == NULL) { + return PSA_ERROR_INVALID_ARGUMENT; + } + esp_fill_random(output, output_size); + *output_length = output_size; + return PSA_SUCCESS; +} + +int mbedtls_platform_get_entropy(unsigned char *output, size_t output_size, + size_t *output_len, size_t *entropy_content) +{ + if (output == NULL || output_size == 0 || output_len == NULL || entropy_content == NULL) { + return -1; + } + + esp_fill_random(output, output_size); + *output_len = output_size; + *entropy_content = 8 * output_size; + return 0; +} +#endif // MBEDTLS_PLATFORM_GET_ENTROPY_ALT diff --git a/components/mbedtls/port/esp_psa_crypto_init.c b/components/mbedtls/port/esp_psa_crypto_init.c new file mode 100644 index 0000000000..a929c82356 --- /dev/null +++ b/components/mbedtls/port/esp_psa_crypto_init.c @@ -0,0 +1,34 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "esp_private/startup_internal.h" +#include "psa/crypto.h" +#include "esp_err.h" +#include "esp_log.h" +#include "sdkconfig.h" + +void mbedtls_psa_crypto_init_include_impl(void); + +/** + * @brief Initialize PSA Crypto library at system startup + * + * This function is called during the SECONDARY initialization stage with priority 104, + * which ensures it runs after esp_security_init (priority 103). This ordering guarantees + * that hardware crypto support is fully initialized before PSA crypto initialization. + */ +ESP_SYSTEM_INIT_FN(mbedtls_psa_crypto_init_fn, SECONDARY, BIT(0), 104) +{ + psa_status_t status = psa_crypto_init(); + if (status != PSA_SUCCESS) { + return ESP_FAIL; + } + return ESP_OK; +} + +void mbedtls_psa_crypto_init_include_impl(void) +{ + // Linker hook, exists for no other purpose +} diff --git a/components/mbedtls/port/include/mbedtls/esp_config.h b/components/mbedtls/port/include/mbedtls/esp_config.h index 611d0878a8..c71e31c306 100644 --- a/components/mbedtls/port/include/mbedtls/esp_config.h +++ b/components/mbedtls/port/include/mbedtls/esp_config.h @@ -36,6 +36,12 @@ * \{ */ +#ifndef CONFIG_IDF_TARGET_LINUX +#define MBEDTLS_PLATFORM_GET_ENTROPY_ALT +#define MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG +#endif // !CONFIG_IDF_TARGET_LINUX +#define MBEDTLS_PSA_ASSUME_EXCLUSIVE_BUFFERS + /** * \def MBEDTLS_HAVE_TIME * diff --git a/examples/protocols/esp_http_client/pytest_esp_http_client.py b/examples/protocols/esp_http_client/pytest_esp_http_client.py index 6c44b8b3f7..911cd45ac3 100644 --- a/examples/protocols/esp_http_client/pytest_esp_http_client.py +++ b/examples/protocols/esp_http_client/pytest_esp_http_client.py @@ -9,16 +9,31 @@ from pytest_embedded_idf.utils import idf_parametrize @pytest.mark.httpbin +@pytest.mark.parametrize( + 'config', + [ + 'default', # mbedTLS crypto backend + 'psa', # PSA crypto backend (tests system integration, HTTPS stack usage) + ], + indirect=True, +) @idf_parametrize('target', ['esp32'], indirect=['target']) def test_examples_protocol_esp_http_client(dut: Dut) -> None: """ steps: | 1. join AP/Ethernet 2. Send HTTP request to httpbin.org + + Tests both mbedTLS and PSA crypto backends. + The PSA config specifically tests: + - PSA crypto initialization + - HTTPS with PSA (stack/heap usage) + - SHA256 Digest Auth with PSA + - Full integration under real workload """ binary_file = os.path.join(dut.app.binary_path, 'esp_http_client_example.bin') bin_size = os.path.getsize(binary_file) - logging.info('esp_http_client_bin_size : {}KB'.format(bin_size // 1024)) + logging.info(f'esp_http_client_bin_size : {bin_size // 1024}KB') # start test dut.expect('Connected to AP, begin http example', timeout=30) dut.expect(r'HTTP GET Status = 200, content_length = (\d)') @@ -46,7 +61,7 @@ def test_examples_protocol_esp_http_client(dut: Dut) -> None: dut.expect(r'HTTP chunk encoding Status = 200, content_length = (-?\d)') # content-len for chunked encoding is typically -1, could be a positive length in some cases dut.expect(r'HTTP Stream reader Status = 200, content_length = (\d)') - dut.expect(r'HTTPS Status = 200, content_length = (\d)') + dut.expect(r'HTTPS Status = 200, content_length = (-?\d)') dut.expect(r'HTTPS Status = 200, content_length = (\d)') dut.expect(r'Last esp error code: 0x8001') dut.expect(r'HTTP GET Status = 200, content_length = (\d)') @@ -71,8 +86,7 @@ def test_examples_protocol_esp_http_client_dynamic_buffer(dut: Dut) -> None: # check and log bin size binary_file = os.path.join(dut.app.binary_path, 'esp_http_client_example.bin') bin_size = os.path.getsize(binary_file) - logging.info('esp_http_client_bin_size : {}KB'.format(bin_size // 1024)) - + logging.info(f'esp_http_client_bin_size : {bin_size // 1024}KB') dut.expect('Connected to AP, begin http example', timeout=30) dut.expect(r'HTTP GET Status = 200, content_length = (\d)') dut.expect(r'HTTP POST Status = 200, content_length = (\d)') diff --git a/examples/protocols/esp_http_client/sdkconfig.ci b/examples/protocols/esp_http_client/sdkconfig.ci index ec5d34b836..8edd2dd114 100644 --- a/examples/protocols/esp_http_client/sdkconfig.ci +++ b/examples/protocols/esp_http_client/sdkconfig.ci @@ -11,3 +11,4 @@ CONFIG_ESP_HTTP_CLIENT_ENABLE_BASIC_AUTH=y CONFIG_ESP_HTTP_CLIENT_ENABLE_DIGEST_AUTH=y CONFIG_EXAMPLE_HTTP_ENDPOINT="httpbin.espressif.cn" CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_CROSS_SIGNED_VERIFY=y +CONFIG_MBEDTLS_SSL_PROTO_TLS1_3=y diff --git a/examples/protocols/esp_http_client/sdkconfig.ci.psa b/examples/protocols/esp_http_client/sdkconfig.ci.psa new file mode 100644 index 0000000000..6cd90037da --- /dev/null +++ b/examples/protocols/esp_http_client/sdkconfig.ci.psa @@ -0,0 +1,19 @@ +# HTTP Client Example with PSA Crypto API +# This config tests the PSA crypto backend instead of direct mbedTLS +CONFIG_EXAMPLE_CONNECT_ETHERNET=y +CONFIG_EXAMPLE_CONNECT_WIFI=n +CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET=y +CONFIG_EXAMPLE_ETH_PHY_GENERIC=y +CONFIG_EXAMPLE_ETH_MDC_GPIO=23 +CONFIG_EXAMPLE_ETH_MDIO_GPIO=18 +CONFIG_EXAMPLE_ETH_PHY_RST_GPIO=5 +CONFIG_EXAMPLE_ETH_PHY_ADDR=1 +CONFIG_EXAMPLE_CONNECT_IPV6=y +CONFIG_ESP_HTTP_CLIENT_ENABLE_BASIC_AUTH=y +CONFIG_ESP_HTTP_CLIENT_ENABLE_DIGEST_AUTH=y +CONFIG_EXAMPLE_HTTP_ENDPOINT="httpbin.espressif.cn" +CONFIG_MBEDTLS_SSL_PROTO_TLS1_3=y +# PSA Crypto Configuration +CONFIG_IDF_EXPERIMENTAL_FEATURES=y +CONFIG_MBEDTLS_ESP_IDF_USE_PSA_CRYPTO=y +CONFIG_ESP_HTTP_CLIENT_PSA_CRYPTO_MIGRATE=y diff --git a/examples/protocols/esp_http_client/sdkconfig.ci.ssldyn b/examples/protocols/esp_http_client/sdkconfig.ci.ssldyn index 83de4f6f35..1924e49e9b 100644 --- a/examples/protocols/esp_http_client/sdkconfig.ci.ssldyn +++ b/examples/protocols/esp_http_client/sdkconfig.ci.ssldyn @@ -12,3 +12,4 @@ CONFIG_MBEDTLS_DYNAMIC_BUFFER=y CONFIG_MBEDTLS_DYNAMIC_FREE_CONFIG_DATA=y CONFIG_MBEDTLS_DHM_C=y CONFIG_EXAMPLE_HTTP_ENDPOINT="httpbin.espressif.cn" +CONFIG_MBEDTLS_SSL_PROTO_TLS1_3=y diff --git a/examples/protocols/http_server/ws_echo_server/CMakeLists.txt b/examples/protocols/http_server/ws_echo_server/CMakeLists.txt index 2b76e0a03e..0a029cab21 100644 --- a/examples/protocols/http_server/ws_echo_server/CMakeLists.txt +++ b/examples/protocols/http_server/ws_echo_server/CMakeLists.txt @@ -2,7 +2,7 @@ # in this exact order for cmake to work correctly cmake_minimum_required(VERSION 3.22) - +list(APPEND sdkconfig_defaults ${CMAKE_CURRENT_LIST_DIR}/ws_echo_server_mbedtls_config.conf) include($ENV{IDF_PATH}/tools/cmake/project.cmake) # "Trim" the build. Include the minimal set of components, main, and anything it depends on. idf_build_set_property(MINIMAL_BUILD ON) diff --git a/examples/protocols/http_server/ws_echo_server/pytest_ws_server_example.py b/examples/protocols/http_server/ws_echo_server/pytest_ws_server_example.py index 000d61987b..eb7ba89b3d 100644 --- a/examples/protocols/http_server/ws_echo_server/pytest_ws_server_example.py +++ b/examples/protocols/http_server/ws_echo_server/pytest_ws_server_example.py @@ -30,7 +30,7 @@ class WsClient: self.uri = uri def __enter__(self): # type: ignore - self.ws.connect('ws://{}:{}/{}'.format(self.ip, self.port, self.uri)) + self.ws.connect(f'ws://{self.ip}:{self.port}/{self.uri}') return self def __exit__(self, exc_type, exc_value, traceback): # type: ignore @@ -48,12 +48,24 @@ class WsClient: @pytest.mark.wifi_router +@pytest.mark.parametrize( + 'config', + [ + 'default', # mbedTLS crypto backend + 'psa', # PSA crypto backend (tests SHA-1 for WebSocket handshake) + ], + indirect=True, +) @idf_parametrize('target', ['esp32'], indirect=['target']) def test_examples_protocol_http_ws_echo_server(dut: Dut) -> None: + """ + Test WebSocket echo server with both mbedTLS and PSA crypto backends. + This specifically tests the SHA-1 computation used in WebSocket handshake (RFC 6455). + """ # Get binary file binary_file = os.path.join(dut.app.binary_path, 'ws_echo_server.bin') bin_size = os.path.getsize(binary_file) - logging.info('http_ws_server_bin_size : {}KB'.format(bin_size // 1024)) + logging.info(f'http_ws_server_bin_size : {bin_size // 1024}KB') logging.info('Starting ws-echo-server test app based on http_server') @@ -68,8 +80,8 @@ def test_examples_protocol_http_ws_echo_server(dut: Dut) -> None: got_ip = dut.expect(r'IPv4 address: (\d+\.\d+\.\d+\.\d+)[^\d]', timeout=30)[1].decode() got_port = dut.expect(r"Starting server on port: '(\d+)'", timeout=30)[1].decode() - logging.info('Got IP : {}'.format(got_ip)) - logging.info('Got Port : {}'.format(got_port)) + logging.info(f'Got IP : {got_ip}') + logging.info(f'Got Port : {got_port}') # Start ws server test with WsClient(got_ip, int(got_port), uri='ws') as ws: @@ -77,32 +89,41 @@ def test_examples_protocol_http_ws_echo_server(dut: Dut) -> None: for expected_opcode in [OPCODE_TEXT, OPCODE_BIN, OPCODE_PING]: ws.write(data=DATA, opcode=expected_opcode) opcode, data = ws.read() - logging.info('Testing opcode {}: Received opcode:{}, data:{}'.format(expected_opcode, opcode, data)) + logging.info(f'Testing opcode {expected_opcode}: Received opcode:{opcode}, data:{data}') data = data.decode() if expected_opcode == OPCODE_PING: dut.expect('Got a WS PING frame, Replying PONG') if opcode != OPCODE_PONG or data != DATA: - raise RuntimeError('Failed to receive correct opcode:{} or data:{}'.format(opcode, data)) + raise RuntimeError(f'Failed to receive correct opcode:{opcode} or data:{data}') continue dut_data = dut.expect(r'Got packet with message: ([A-Za-z0-9_]*)')[1] dut_opcode = dut.expect(r'Packet type: ([0-9]*)')[1].decode() if opcode != expected_opcode or data != DATA or opcode != int(dut_opcode) or (data not in str(dut_data)): - raise RuntimeError('Failed to receive correct opcode:{} or data:{}'.format(opcode, data)) + raise RuntimeError(f'Failed to receive correct opcode:{opcode} or data:{data}') ws.write(data='Trigger async', opcode=OPCODE_TEXT) opcode, data = ws.read() - logging.info('Testing async send: Received opcode:{}, data:{}'.format(opcode, data)) + logging.info(f'Testing async send: Received opcode:{opcode}, data:{data}') data = data.decode() if opcode != OPCODE_TEXT or data != 'Async data': - raise RuntimeError('Failed to receive correct opcode:{} or data:{}'.format(opcode, data)) + raise RuntimeError(f'Failed to receive correct opcode:{opcode} or data:{data}') @pytest.mark.wifi_router +@pytest.mark.parametrize( + 'config', + [ + 'default', # mbedTLS crypto backend + 'psa', # PSA crypto backend (tests SHA-1 for WebSocket handshake) + ], + indirect=True, +) @idf_parametrize('target', ['esp32'], indirect=['target']) def test_ws_auth_handshake(dut: Dut) -> None: """ Test that connecting to /ws does NOT print the handshake success log. This is used to verify ws_pre_handshake_cb can reject the handshake. + Tests both mbedTLS and PSA crypto backends. """ # Wait for device to connect and start server if dut.app.sdkconfig.get('EXAMPLE_WIFI_SSID_PWD_FROM_STDIN') is True: diff --git a/examples/protocols/http_server/ws_echo_server/sdkconfig.ci.psa b/examples/protocols/http_server/ws_echo_server/sdkconfig.ci.psa new file mode 100644 index 0000000000..20ce00c2ab --- /dev/null +++ b/examples/protocols/http_server/ws_echo_server/sdkconfig.ci.psa @@ -0,0 +1,8 @@ +# WebSocket Echo Server Example with PSA Crypto API +# This config tests the PSA crypto backend for WebSocket handshake (SHA-1) +CONFIG_LOG_DEFAULT_LEVEL_DEBUG=y +CONFIG_EXAMPLE_WIFI_SSID_PWD_FROM_STDIN=y +# PSA Crypto Configuration +CONFIG_IDF_EXPERIMENTAL_FEATURES=y +CONFIG_MBEDTLS_ESP_IDF_USE_PSA_CRYPTO=y +CONFIG_HTTPD_SERVER_PSA_CRYPTO_MIGRATE=y diff --git a/examples/protocols/http_server/ws_echo_server/ws_echo_server_mbedtls_config.conf b/examples/protocols/http_server/ws_echo_server/ws_echo_server_mbedtls_config.conf new file mode 100644 index 0000000000..b2319d6c63 --- /dev/null +++ b/examples/protocols/http_server/ws_echo_server/ws_echo_server_mbedtls_config.conf @@ -0,0 +1,8 @@ +CONFIG_MBEDTLS_TLS_ENABLED=n +CONFIG_MBEDTLS_MD5_C=n +CONFIG_MBEDTLS_SHA224_C=n +CONFIG_MBEDTLS_SHA256_C=n +CONFIG_MBEDTLS_SHA384_C=n +CONFIG_MBEDTLS_SHA512_C=n +CONFIG_MBEDTLS_SHA3_C=n +CONFIG_MBEDTLS_ROM_MD5=n