diff --git a/components/esp-tls/CMakeLists.txt b/components/esp-tls/CMakeLists.txt index 3809b6473b..b5eabfe4dc 100644 --- a/components/esp-tls/CMakeLists.txt +++ b/components/esp-tls/CMakeLists.txt @@ -4,9 +4,9 @@ if(CONFIG_ESP_TLS_USING_MBEDTLS) "esp_tls_mbedtls.c") endif() -if(CONFIG_ESP_TLS_USING_WOLFSSL) +if(CONFIG_ESP_TLS_CUSTOM_STACK) list(APPEND srcs - "esp_tls_wolfssl.c") + "esp_tls_custom_stack.c") endif() set(priv_req http_parser esp_timer) @@ -17,15 +17,9 @@ endif() idf_component_register(SRCS "${srcs}" INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR} esp-tls-crypto PRIV_INCLUDE_DIRS "private_include" - # mbedtls is public requirements because esp_tls.h - # includes mbedtls header files. REQUIRES mbedtls PRIV_REQUIRES ${priv_req}) -if(CONFIG_ESP_TLS_USING_WOLFSSL) - idf_component_get_property(wolfssl esp-wolfssl COMPONENT_LIB) - target_link_libraries(${COMPONENT_LIB} PUBLIC ${wolfssl}) -endif() if(NOT ${IDF_TARGET} STREQUAL "linux") # Increase link multiplicity to get some lwip symbols correctly resolved by the linker diff --git a/components/esp-tls/Kconfig b/components/esp-tls/Kconfig index 6f70f8440e..ef9904e3a4 100644 --- a/components/esp-tls/Kconfig +++ b/components/esp-tls/Kconfig @@ -3,15 +3,23 @@ menu "ESP-TLS" prompt "Choose SSL/TLS library for ESP-TLS (See help for more Info)" default ESP_TLS_USING_MBEDTLS help - The ESP-TLS APIs support multiple backend TLS libraries. Currently mbedTLS and WolfSSL are - supported. Different TLS libraries may support different features and have different resource - usage. Consult the ESP-TLS documentation in ESP-IDF Programming guide for more details. + The ESP-TLS APIs support multiple backend TLS libraries. mbedTLS is supported by default. + Custom TLS stacks can be registered via esp_tls_register_stack() API when + CONFIG_ESP_TLS_CUSTOM_STACK is selected. Different TLS libraries may support different + features and have different resource usage. Consult the ESP-TLS documentation in ESP-IDF + Programming guide for more details. config ESP_TLS_USING_MBEDTLS bool "mbedTLS" select MBEDTLS_TLS_ENABLED - config ESP_TLS_USING_WOLFSSL - depends on TLS_STACK_WOLFSSL - bool "wolfSSL (License info in wolfSSL directory README)" + config ESP_TLS_CUSTOM_STACK + bool "Custom TLS stack (register via esp_tls_register_stack())" + help + When selected, allows external components to register their own TLS stack implementation + via esp_tls_register_stack() API. The custom stack must be registered before creating + any TLS connections, otherwise TLS operations will fail. + + External components can provide any TLS stack implementation by implementing the + esp_tls_stack_ops_t interface. endchoice config ESP_TLS_USE_SECURE_ELEMENT @@ -80,7 +88,7 @@ menu "ESP-TLS" select MBEDTLS_KEY_EXCHANGE_ECDHE_PSK if ESP_TLS_USING_MBEDTLS && MBEDTLS_ECDH_C help Enable support for pre shared key ciphers, supported for both mbedTLS as well as - wolfSSL TLS library. + custom TLS stacks. config ESP_TLS_INSECURE bool "Allow potentially insecure options" @@ -101,19 +109,6 @@ menu "ESP-TLS" with a server which has a fake identity, provided that the server certificate is not provided either through API or other mechanism like ca_store etc. - config ESP_DEBUG_WOLFSSL - bool "Enable debug logs for wolfSSL" - depends on ESP_TLS_USING_WOLFSSL - help - Enable detailed debug prints for wolfSSL SSL library. - - config ESP_TLS_OCSP_CHECKALL - bool "Enabled full OCSP checks for ESP-TLS" - depends on ESP_TLS_USING_WOLFSSL - default y - help - Enable a fuller set of OCSP checks: checking revocation status of intermediate certificates, - optional fallbacks to CRLs, etc. config ESP_TLS_DYN_BUF_STRATEGY_SUPPORTED bool diff --git a/components/esp-tls/esp-tls-crypto/esp_tls_crypto.c b/components/esp-tls/esp-tls-crypto/esp_tls_crypto.c index 8589164cc5..6fdea8557e 100644 --- a/components/esp-tls/esp-tls-crypto/esp_tls_crypto.c +++ b/components/esp-tls/esp-tls-crypto/esp_tls_crypto.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2020-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -8,24 +8,21 @@ #include "esp_log.h" #include "esp_err.h" #include "sdkconfig.h" -__attribute__((unused)) static const char *TAG = "esp_crypto"; -#ifdef CONFIG_ESP_TLS_USING_MBEDTLS + +#if CONFIG_ESP_TLS_USING_MBEDTLS #include "mbedtls/base64.h" #include "mbedtls/error.h" #include "psa/crypto.h" -#define _esp_crypto_sha1 esp_crypto_sha1_mbedtls -#define _esp_crypto_base64_encode esp_crypto_bas64_encode_mbedtls -#elif CONFIG_ESP_TLS_USING_WOLFSSL -#include "wolfssl/ssl.h" /* SHA functions are listed in wolfssl/ssl.h */ -#include "wolfssl/wolfcrypt/coding.h" -#define _esp_crypto_sha1 esp_crypto_sha1_wolfSSL -#define _esp_crypto_base64_encode esp_crypto_base64_encode_woflSSL #endif -#ifdef CONFIG_ESP_TLS_USING_MBEDTLS -static int esp_crypto_sha1_mbedtls( const unsigned char *input, - size_t ilen, - unsigned char output[20]) +#if CONFIG_ESP_TLS_CUSTOM_STACK +#include "esp_tls_custom_stack.h" +#endif + +#if CONFIG_ESP_TLS_USING_MBEDTLS +static int esp_crypto_sha1_mbedtls(const unsigned char *input, + size_t ilen, + unsigned char output[20]) { #if CONFIG_MBEDTLS_SHA1_C || CONFIG_MBEDTLS_HARDWARE_SHA psa_hash_operation_t ctx = PSA_HASH_OPERATION_INIT; @@ -48,51 +45,52 @@ exit: psa_hash_abort(&ctx); return status == PSA_SUCCESS ? 0 : -1; #else - ESP_LOGE(TAG, "Please enable CONFIG_MBEDTLS_SHA1_C or CONFIG_MBEDTLS_HARDWARE_SHA to support SHA1 operations"); + ESP_LOGE("esp_crypto", "Please enable CONFIG_MBEDTLS_SHA1_C or CONFIG_MBEDTLS_HARDWARE_SHA to support SHA1 operations"); return MBEDTLS_ERR_PLATFORM_FEATURE_UNSUPPORTED; #endif /* CONFIG_MBEDTLS_SHA1_C || CONFIG_MBEDTLS_HARDWARE_SHA*/ } -static int esp_crypto_bas64_encode_mbedtls( unsigned char *dst, size_t dlen, - size_t *olen, const unsigned char *src, - size_t slen) +static int esp_crypto_base64_encode_mbedtls(unsigned char *dst, size_t dlen, + size_t *olen, const unsigned char *src, + size_t slen) { return mbedtls_base64_encode(dst, dlen, olen, src, slen); } +#endif /* CONFIG_ESP_TLS_USING_MBEDTLS */ -#elif CONFIG_ESP_TLS_USING_WOLFSSL -static int esp_crypto_sha1_wolfSSL( const unsigned char *input, - size_t ilen, - unsigned char output[20]) +int esp_crypto_sha1(const unsigned char *input, + size_t ilen, + unsigned char output[20]) { - unsigned char *ret = wolfSSL_SHA1(input, ilen, output); - if (ret == NULL) { - ESP_LOGE(TAG, "Error in calculating sha1 sum"); +#if CONFIG_ESP_TLS_USING_MBEDTLS + return esp_crypto_sha1_mbedtls(input, ilen, output); +#elif CONFIG_ESP_TLS_CUSTOM_STACK + int ret = esp_tls_custom_stack_crypto_sha1(input, ilen, output); + if (ret == ESP_ERR_NOT_SUPPORTED) { + ESP_LOGE("esp_crypto", "Custom TLS stack must implement crypto_sha1 callback"); return -1; } - return 0; -} - -static int esp_crypto_base64_encode_woflSSL(unsigned char *dst, size_t dlen, size_t *olen, - const unsigned char *src, size_t slen) -{ - *olen = dlen; - return Base64_Encode_NoNl((const byte *) src, (word32) slen, (byte *) dst, (word32 *) olen); -} - + return ret; #else -#error "No TLS/SSL Stack selected" + ESP_LOGE("esp_crypto", "No TLS stack configured"); + return -1; #endif - -int esp_crypto_sha1( const unsigned char *input, - size_t ilen, - unsigned char output[20]) -{ - return _esp_crypto_sha1(input, ilen, output); } int esp_crypto_base64_encode(unsigned char *dst, size_t dlen, size_t *olen, - const unsigned char *src, size_t slen ) + const unsigned char *src, size_t slen) { - return _esp_crypto_base64_encode(dst, dlen, olen, src, slen); +#if CONFIG_ESP_TLS_USING_MBEDTLS + return esp_crypto_base64_encode_mbedtls(dst, dlen, olen, src, slen); +#elif CONFIG_ESP_TLS_CUSTOM_STACK + int ret = esp_tls_custom_stack_crypto_base64_encode(dst, dlen, olen, src, slen); + if (ret == ESP_ERR_NOT_SUPPORTED) { + ESP_LOGE("esp_crypto", "Custom TLS stack must implement crypto_base64_encode callback"); + return -1; + } + return ret; +#else + ESP_LOGE("esp_crypto", "No TLS stack configured"); + return -1; +#endif } diff --git a/components/esp-tls/esp-tls-crypto/esp_tls_crypto.h b/components/esp-tls/esp-tls-crypto/esp_tls_crypto.h index 803023ecdd..b700b55ee6 100644 --- a/components/esp-tls/esp-tls-crypto/esp_tls_crypto.h +++ b/components/esp-tls/esp-tls-crypto/esp_tls_crypto.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2020-2021 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2020-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -22,12 +22,9 @@ extern "C" { * @param[out] output calculated sha1 sum * * @return - * mbedtls stack:- - * - MBEDTLS_ERR_SHA1_BAD_INPUT_DATA on BAD INPUT. - * - 0 on success. - * wolfssl stack:- - * - -1 on failure. - * - 0 on success. + * - 0 if successful. + * - -1 if error. + * - MBEDTLS_ERR_PLATFORM_FEATURE_UNSUPPORTED if SHA1 is not supported. */ int esp_crypto_sha1(const unsigned char *input, size_t ilen, @@ -43,12 +40,8 @@ int esp_crypto_sha1(const unsigned char *input, * @param[in] slen src buffer len * * @return - * mbedtls stack:- * - MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL if buffer is of insufficient size. * - 0 if successful. - * wolfssl stack:- - * - <0 on failure. - * - 0 if succcessful. */ int esp_crypto_base64_encode(unsigned char *dst, size_t dlen, size_t *olen, const unsigned char *src, diff --git a/components/esp-tls/esp_tls.c b/components/esp-tls/esp_tls.c index e228809b12..f18484067c 100644 --- a/components/esp-tls/esp_tls.c +++ b/components/esp-tls/esp_tls.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2019-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2019-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -51,8 +51,8 @@ static const char *TAG = "esp-tls"; #ifdef CONFIG_ESP_TLS_USING_MBEDTLS #include "esp_tls_mbedtls.h" -#elif CONFIG_ESP_TLS_USING_WOLFSSL -#include "esp_tls_wolfssl.h" +#elif CONFIG_ESP_TLS_CUSTOM_STACK +#include "esp_tls_custom_stack.h" #endif #ifdef ESP_PLATFORM @@ -80,25 +80,33 @@ static const char *TAG = "esp-tls"; #define _esp_tls_server_session_ticket_ctx_free esp_mbedtls_server_session_ticket_ctx_free #define _esp_tls_get_bytes_avail esp_mbedtls_get_bytes_avail #define _esp_tls_init_global_ca_store esp_mbedtls_init_global_ca_store -#define _esp_tls_set_global_ca_store esp_mbedtls_set_global_ca_store /*!< Callback function for setting global CA store data for TLS/SSL */ +#define _esp_tls_set_global_ca_store esp_mbedtls_set_global_ca_store #define _esp_tls_get_global_ca_store esp_mbedtls_get_global_ca_store -#define _esp_tls_free_global_ca_store esp_mbedtls_free_global_ca_store /*!< Callback function for freeing global ca store for TLS/SSL */ +#define _esp_tls_free_global_ca_store esp_mbedtls_free_global_ca_store #define _esp_tls_get_ciphersuites_list esp_mbedtls_get_ciphersuites_list -#elif CONFIG_ESP_TLS_USING_WOLFSSL /* CONFIG_ESP_TLS_USING_MBEDTLS */ -#define _esp_create_ssl_handle esp_create_wolfssl_handle -#define _esp_tls_handshake esp_wolfssl_handshake -#define _esp_tls_read esp_wolfssl_read -#define _esp_tls_write esp_wolfssl_write -#define _esp_tls_conn_delete esp_wolfssl_conn_delete -#define _esp_tls_net_init esp_wolfssl_net_init -#define _esp_tls_server_session_create esp_wolfssl_server_session_create -#define _esp_tls_server_session_delete esp_wolfssl_server_session_delete -#define _esp_tls_get_bytes_avail esp_wolfssl_get_bytes_avail -#define _esp_tls_init_global_ca_store esp_wolfssl_init_global_ca_store -#define _esp_tls_set_global_ca_store esp_wolfssl_set_global_ca_store /*!< Callback function for setting global CA store data for TLS/SSL */ -#define _esp_tls_free_global_ca_store esp_wolfssl_free_global_ca_store /*!< Callback function for freeing global ca store for TLS/SSL */ -#define _esp_tls_get_ssl_context esp_wolfssl_get_ssl_context -#else /* ESP_TLS_USING_WOLFSSL */ +#elif CONFIG_ESP_TLS_CUSTOM_STACK +#define _esp_create_ssl_handle esp_tls_custom_stack_create_ssl_handle +#define _esp_tls_handshake esp_tls_custom_stack_handshake +#define _esp_tls_read esp_tls_custom_stack_read +#define _esp_tls_write esp_tls_custom_stack_write +#define _esp_tls_conn_delete esp_tls_custom_stack_conn_delete +#define _esp_tls_net_init esp_tls_custom_stack_net_init +#define _esp_tls_get_client_session esp_tls_custom_stack_get_client_session +#define _esp_tls_free_client_session esp_tls_custom_stack_free_client_session +#define _esp_tls_get_ssl_context esp_tls_custom_stack_get_ssl_context +#define _esp_tls_server_session_create esp_tls_custom_stack_server_session_create +#define _esp_tls_server_session_init esp_tls_custom_stack_server_session_init +#define _esp_tls_server_session_continue_async esp_tls_custom_stack_server_session_continue_async +#define _esp_tls_server_session_delete esp_tls_custom_stack_server_session_delete +#define _esp_tls_server_session_ticket_ctx_init esp_tls_custom_stack_server_session_ticket_ctx_init +#define _esp_tls_server_session_ticket_ctx_free esp_tls_custom_stack_server_session_ticket_ctx_free +#define _esp_tls_get_bytes_avail esp_tls_custom_stack_get_bytes_avail +#define _esp_tls_init_global_ca_store esp_tls_custom_stack_init_global_ca_store +#define _esp_tls_set_global_ca_store esp_tls_custom_stack_set_global_ca_store +#define _esp_tls_get_global_ca_store esp_tls_custom_stack_get_global_ca_store +#define _esp_tls_free_global_ca_store esp_tls_custom_stack_free_global_ca_store +#define _esp_tls_get_ciphersuites_list esp_tls_custom_stack_get_ciphersuites_list +#else #error "No TLS stack configured" #endif diff --git a/components/esp-tls/esp_tls.h b/components/esp-tls/esp_tls.h index a72c33ff05..1b3af1a6c0 100644 --- a/components/esp-tls/esp_tls.h +++ b/components/esp-tls/esp_tls.h @@ -16,9 +16,6 @@ #ifdef CONFIG_ESP_TLS_SERVER_SESSION_TICKETS #include "mbedtls/ssl_ticket.h" #endif -#elif CONFIG_ESP_TLS_USING_WOLFSSL -#include "wolfssl/wolfcrypt/settings.h" -#include "wolfssl/ssl.h" #endif diff --git a/components/esp-tls/esp_tls_custom_stack.c b/components/esp-tls/esp_tls_custom_stack.c new file mode 100644 index 0000000000..ac4e92edfe --- /dev/null +++ b/components/esp-tls/esp_tls_custom_stack.c @@ -0,0 +1,282 @@ +/* + * SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "sdkconfig.h" + +#if CONFIG_ESP_TLS_CUSTOM_STACK + +#include "esp_tls_custom_stack.h" +#include "esp_tls_private.h" +#include "esp_log.h" +#include +#include + +static const char *TAG = "esp-tls-custom-stack"; + +static const esp_tls_stack_ops_t *s_esp_tls_custom_stack = NULL; +static void *s_esp_tls_custom_stack_user_ctx = NULL; + +#define CHECK_STACK_REGISTERED(ret_val) \ + do { \ + if (s_esp_tls_custom_stack == NULL) { \ + ESP_LOGE(TAG, "No TLS stack registered. Call esp_tls_register_stack() first."); \ + return ret_val; \ + } \ + } while (0) + +#define CHECK_STACK_REGISTERED_VOID() \ + do { \ + if (s_esp_tls_custom_stack == NULL) { \ + ESP_LOGE(TAG, "No TLS stack registered. Call esp_tls_register_stack() first."); \ + return; \ + } \ + } while (0) + +esp_err_t esp_tls_register_stack(const esp_tls_stack_ops_t *ops, void *user_ctx) +{ + if (!ops) { + return ESP_ERR_INVALID_ARG; + } + + /* Validate version */ + if (ops->version != ESP_TLS_STACK_OPS_VERSION) { + ESP_LOGE(TAG, "Invalid TLS stack version: expected %d, got %" PRIu32, + ESP_TLS_STACK_OPS_VERSION, ops->version); + return ESP_ERR_INVALID_VERSION; + } + + /* Validate required function pointers */ + if (!ops->create_ssl_handle || !ops->handshake || !ops->read || !ops->write || + !ops->conn_delete || !ops->net_init || !ops->get_ssl_context || + !ops->get_bytes_avail || !ops->init_global_ca_store || + !ops->set_global_ca_store || !ops->get_global_ca_store || + !ops->free_global_ca_store || !ops->get_ciphersuites_list) { + ESP_LOGE(TAG, "Invalid TLS stack operations: required function pointers are NULL"); + return ESP_ERR_INVALID_ARG; + } + + if (s_esp_tls_custom_stack != NULL) { + ESP_LOGE(TAG, "TLS stack already registered"); + return ESP_ERR_INVALID_STATE; + } + + s_esp_tls_custom_stack = ops; + s_esp_tls_custom_stack_user_ctx = user_ctx; + ESP_LOGI(TAG, "Custom TLS stack registered successfully"); + return ESP_OK; +} + +const esp_tls_stack_ops_t *esp_tls_get_registered_stack(void) +{ + return s_esp_tls_custom_stack; +} + +esp_err_t esp_tls_unregister_stack(void) +{ + if (s_esp_tls_custom_stack == NULL) { + ESP_LOGW(TAG, "No TLS stack registered"); + return ESP_ERR_INVALID_STATE; + } + + s_esp_tls_custom_stack = NULL; + s_esp_tls_custom_stack_user_ctx = NULL; + ESP_LOGI(TAG, "Custom TLS stack unregistered successfully"); + return ESP_OK; +} + +esp_err_t esp_tls_custom_stack_create_ssl_handle(const char *hostname, size_t hostlen, const void *cfg, esp_tls_t *tls, void *server_params) +{ + CHECK_STACK_REGISTERED(ESP_ERR_INVALID_STATE); + return s_esp_tls_custom_stack->create_ssl_handle(s_esp_tls_custom_stack_user_ctx, hostname, hostlen, cfg, tls, server_params); +} + +int esp_tls_custom_stack_handshake(esp_tls_t *tls, const esp_tls_cfg_t *cfg) +{ + CHECK_STACK_REGISTERED(-1); + return s_esp_tls_custom_stack->handshake(s_esp_tls_custom_stack_user_ctx, tls, cfg); +} + +ssize_t esp_tls_custom_stack_read(esp_tls_t *tls, char *data, size_t datalen) +{ + CHECK_STACK_REGISTERED(-1); + return s_esp_tls_custom_stack->read(s_esp_tls_custom_stack_user_ctx, tls, data, datalen); +} + +ssize_t esp_tls_custom_stack_write(esp_tls_t *tls, const char *data, size_t datalen) +{ + CHECK_STACK_REGISTERED(-1); + return s_esp_tls_custom_stack->write(s_esp_tls_custom_stack_user_ctx, tls, data, datalen); +} + +void esp_tls_custom_stack_conn_delete(esp_tls_t *tls) +{ + CHECK_STACK_REGISTERED_VOID(); + s_esp_tls_custom_stack->conn_delete(s_esp_tls_custom_stack_user_ctx, tls); +} + +void esp_tls_custom_stack_net_init(esp_tls_t *tls) +{ + CHECK_STACK_REGISTERED_VOID(); + s_esp_tls_custom_stack->net_init(s_esp_tls_custom_stack_user_ctx, tls); +} + +void *esp_tls_custom_stack_get_ssl_context(esp_tls_t *tls) +{ + CHECK_STACK_REGISTERED(NULL); + return s_esp_tls_custom_stack->get_ssl_context(s_esp_tls_custom_stack_user_ctx, tls); +} + +ssize_t esp_tls_custom_stack_get_bytes_avail(esp_tls_t *tls) +{ + CHECK_STACK_REGISTERED(-1); + return s_esp_tls_custom_stack->get_bytes_avail(s_esp_tls_custom_stack_user_ctx, tls); +} + +esp_err_t esp_tls_custom_stack_init_global_ca_store(void) +{ + CHECK_STACK_REGISTERED(ESP_ERR_INVALID_STATE); + return s_esp_tls_custom_stack->init_global_ca_store(s_esp_tls_custom_stack_user_ctx); +} + +esp_err_t esp_tls_custom_stack_set_global_ca_store(const unsigned char *cacert_pem_buf, const unsigned int cacert_pem_bytes) +{ + CHECK_STACK_REGISTERED(ESP_ERR_INVALID_STATE); + return s_esp_tls_custom_stack->set_global_ca_store(s_esp_tls_custom_stack_user_ctx, cacert_pem_buf, cacert_pem_bytes); +} + +void *esp_tls_custom_stack_get_global_ca_store(void) +{ + CHECK_STACK_REGISTERED(NULL); + return s_esp_tls_custom_stack->get_global_ca_store(s_esp_tls_custom_stack_user_ctx); +} + +void esp_tls_custom_stack_free_global_ca_store(void) +{ + CHECK_STACK_REGISTERED_VOID(); + s_esp_tls_custom_stack->free_global_ca_store(s_esp_tls_custom_stack_user_ctx); +} + +const int *esp_tls_custom_stack_get_ciphersuites_list(void) +{ + CHECK_STACK_REGISTERED(NULL); + return s_esp_tls_custom_stack->get_ciphersuites_list(s_esp_tls_custom_stack_user_ctx); +} + +void *esp_tls_custom_stack_get_client_session(esp_tls_t *tls) +{ + if (s_esp_tls_custom_stack == NULL || !s_esp_tls_custom_stack->get_client_session) { + ESP_LOGE(TAG, "No TLS stack registered or client session not supported."); + return NULL; + } + return s_esp_tls_custom_stack->get_client_session(s_esp_tls_custom_stack_user_ctx, tls); +} + +void esp_tls_custom_stack_free_client_session(void *client_session) +{ + if (s_esp_tls_custom_stack == NULL || !s_esp_tls_custom_stack->free_client_session) { + ESP_LOGE(TAG, "No TLS stack registered or client session not supported."); + return; + } + s_esp_tls_custom_stack->free_client_session(s_esp_tls_custom_stack_user_ctx, client_session); +} + +esp_err_t esp_tls_custom_stack_server_session_ticket_ctx_init(void *cfg) +{ + if (s_esp_tls_custom_stack == NULL || !s_esp_tls_custom_stack->server_session_ticket_ctx_init) { + ESP_LOGE(TAG, "No TLS stack registered or server session tickets not supported."); + return ESP_ERR_NOT_SUPPORTED; + } + return s_esp_tls_custom_stack->server_session_ticket_ctx_init(s_esp_tls_custom_stack_user_ctx, cfg); +} + +void esp_tls_custom_stack_server_session_ticket_ctx_free(void *cfg) +{ + if (s_esp_tls_custom_stack == NULL || !s_esp_tls_custom_stack->server_session_ticket_ctx_free) { + ESP_LOGE(TAG, "No TLS stack registered or server session tickets not supported."); + return; + } + s_esp_tls_custom_stack->server_session_ticket_ctx_free(s_esp_tls_custom_stack_user_ctx, cfg); +} + +int esp_tls_custom_stack_server_session_create(esp_tls_cfg_server_t *cfg, int sockfd, esp_tls_t *tls) +{ + if (s_esp_tls_custom_stack == NULL || !s_esp_tls_custom_stack->server_session_create) { + ESP_LOGE(TAG, "No TLS stack registered or server session not supported."); + return -1; + } + return s_esp_tls_custom_stack->server_session_create(s_esp_tls_custom_stack_user_ctx, cfg, sockfd, tls); +} + +esp_err_t esp_tls_custom_stack_server_session_init(esp_tls_cfg_server_t *cfg, int sockfd, esp_tls_t *tls) +{ + if (s_esp_tls_custom_stack == NULL || !s_esp_tls_custom_stack->server_session_init) { + ESP_LOGE(TAG, "No TLS stack registered or server session not supported."); + return ESP_ERR_INVALID_STATE; + } + return s_esp_tls_custom_stack->server_session_init(s_esp_tls_custom_stack_user_ctx, cfg, sockfd, tls); +} + +int esp_tls_custom_stack_server_session_continue_async(esp_tls_t *tls) +{ + if (s_esp_tls_custom_stack == NULL || !s_esp_tls_custom_stack->server_session_continue_async) { + ESP_LOGE(TAG, "No TLS stack registered or server session not supported."); + return -1; + } + return s_esp_tls_custom_stack->server_session_continue_async(s_esp_tls_custom_stack_user_ctx, tls); +} + +void esp_tls_custom_stack_server_session_delete(esp_tls_t *tls) +{ + CHECK_STACK_REGISTERED_VOID(); + if (s_esp_tls_custom_stack->server_session_delete) { + s_esp_tls_custom_stack->server_session_delete(s_esp_tls_custom_stack_user_ctx, tls); + } else { + /* Fall back to conn_delete as documented */ + s_esp_tls_custom_stack->conn_delete(s_esp_tls_custom_stack_user_ctx, tls); + } +} + +int esp_tls_custom_stack_crypto_sha1(const unsigned char *input, size_t ilen, unsigned char output[20]) +{ + if (s_esp_tls_custom_stack == NULL || !s_esp_tls_custom_stack->crypto_sha1) { + return ESP_ERR_NOT_SUPPORTED; + } + return s_esp_tls_custom_stack->crypto_sha1(s_esp_tls_custom_stack_user_ctx, input, ilen, output); +} + +int esp_tls_custom_stack_crypto_base64_encode(unsigned char *dst, size_t dlen, size_t *olen, + const unsigned char *src, size_t slen) +{ + if (s_esp_tls_custom_stack == NULL || !s_esp_tls_custom_stack->crypto_base64_encode) { + return ESP_ERR_NOT_SUPPORTED; + } + return s_esp_tls_custom_stack->crypto_base64_encode(s_esp_tls_custom_stack_user_ctx, dst, dlen, olen, src, slen); +} + +#else /* CONFIG_ESP_TLS_CUSTOM_STACK */ + +#include "esp_err.h" +#include "esp_tls_custom_stack.h" + +/* Stub implementations when custom stack is disabled */ +esp_err_t esp_tls_register_stack(const esp_tls_stack_ops_t *ops, void *user_ctx) +{ + (void)ops; + (void)user_ctx; + return ESP_ERR_NOT_SUPPORTED; +} + +const esp_tls_stack_ops_t *esp_tls_get_registered_stack(void) +{ + return NULL; +} + +esp_err_t esp_tls_unregister_stack(void) +{ + return ESP_ERR_NOT_SUPPORTED; +} + +#endif /* CONFIG_ESP_TLS_CUSTOM_STACK */ diff --git a/components/esp-tls/esp_tls_custom_stack.h b/components/esp-tls/esp_tls_custom_stack.h new file mode 100644 index 0000000000..83bcc9c23f --- /dev/null +++ b/components/esp-tls/esp_tls_custom_stack.h @@ -0,0 +1,734 @@ +/* + * SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "sdkconfig.h" +#include "esp_assert.h" +#include "esp_err.h" +#include "esp_tls.h" +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Version of the TLS stack operations structure + * + * This version number must be set in the `version` field of esp_tls_stack_ops_t + * when registering a custom TLS stack. It allows ESP-TLS to detect incompatible + * interface changes between ESP-IDF versions and custom stack implementations. + */ +#define ESP_TLS_STACK_OPS_VERSION 1 + +/** + * @brief TLS stack operations vtable + * + * This structure defines the interface that external TLS stack implementations + * must provide. All function pointers must be non-NULL. + * + * @note To register a custom TLS stack: + * 1. Enable CONFIG_ESP_TLS_CUSTOM_STACK in menuconfig + * 2. Implement all required functions in this structure + * 3. Call esp_tls_register_stack() with your implementation before creating any TLS connections + * + * @note The TLS context (esp_tls_t) is managed by ESP-TLS. Your implementation should store + * stack-specific data in the appropriate fields of esp_tls_t (e.g., priv_ctx, priv_ssl) + * or allocate additional memory and store a pointer to it. + * + * @note For client connections: hostname and cfg (esp_tls_cfg_t) are provided + * @note For server connections: hostname is NULL, cfg is esp_tls_cfg_server_t, server_params is provided + */ +typedef struct esp_tls_stack_ops { + /** + * @brief Structure version for compatibility checking + * + * Must be set to ESP_TLS_STACK_OPS_VERSION. This field allows ESP-TLS to + * detect version mismatches between the ESP-IDF version and the custom + * TLS stack implementation. + */ + uint32_t version; + + /** + * @brief Create SSL handle for a TLS connection + * + * This function initializes the TLS stack for a new connection. It should: + * - Initialize the TLS/SSL context for the given connection + * - Configure certificates, keys, and other TLS parameters from cfg + * - Set up the underlying socket (tls->sockfd is already set) + * - For client: configure SNI using hostname + * - For server: use server_params if provided + * - Store stack-specific context in tls->priv_ctx or tls->priv_ssl + * + * @param user_ctx User context pointer passed to esp_tls_register_stack() + * @param hostname Hostname for the connection (client only, NULL for server) + * @param hostlen Length of hostname (0 for server) + * @param cfg TLS configuration: + * - For client: pointer to esp_tls_cfg_t + * - For server: pointer to esp_tls_cfg_server_t + * @param tls TLS context (already allocated, sockfd is set) + * @param server_params Server-specific parameters (NULL for client, non-NULL for server) + * Contains server configuration callbacks + * @return + * - ESP_OK: SSL handle created successfully + * - ESP_ERR_NO_MEM: Memory allocation failed + * - ESP_ERR_INVALID_ARG: Invalid configuration + * - Other error codes: Stack-specific errors + */ + esp_err_t (*create_ssl_handle)(void *user_ctx, const char *hostname, size_t hostlen, const void *cfg, esp_tls_t *tls, void *server_params); + + /** + * @brief Perform TLS handshake + * + * This function performs the TLS handshake. It should: + * - Execute the TLS handshake protocol + * - Handle handshake state (may need multiple calls) + * - Verify certificates if configured + * - Update tls->conn_state appropriately + * + * @param user_ctx User context pointer passed to esp_tls_register_stack() + * @param tls TLS context (created by create_ssl_handle) + * @param cfg TLS configuration (esp_tls_cfg_t for client, can be used for timeout checks) + * + * @return + * - 1: Handshake completed successfully + * - 0: Handshake in progress, call again later + * - ESP_TLS_ERR_SSL_WANT_READ: Need to read more data from socket + * - ESP_TLS_ERR_SSL_WANT_WRITE: Need to write more data to socket + * - Negative value: Handshake failed (error code) + * + * @note This function may be called multiple times until it returns 1 or a negative error. + * The ESP-TLS layer handles retries based on the return value. + */ + int (*handshake)(void *user_ctx, esp_tls_t *tls, const esp_tls_cfg_t *cfg); + + /** + * @brief Read data from TLS connection + * + * This function reads decrypted application data from the TLS connection. + * It should: + * - Read encrypted data from tls->sockfd + * - Decrypt using the TLS stack + * - Return decrypted application data + * - Handle partial reads and blocking/non-blocking behavior + * + * @param user_ctx User context pointer passed to esp_tls_register_stack() + * @param tls TLS context (handshake must be completed) + * @param data Buffer to store decrypted data + * @param datalen Maximum number of bytes to read (size of data buffer) + * + * @return + * - Positive value: Number of bytes read (0 < return <= datalen) + * - 0: Connection closed by peer + * - ESP_TLS_ERR_SSL_WANT_READ: Need to read more encrypted data from socket + * - ESP_TLS_ERR_SSL_WANT_WRITE: Need to write data (renegotiation) + * - Negative value: Error occurred + * + * @note This function is called by tls->read() callback, which is set during handshake. + * @note The function should handle TLS record boundaries and may need multiple socket reads + * to decrypt a single application data record. + */ + ssize_t (*read)(void *user_ctx, esp_tls_t *tls, char *data, size_t datalen); + + /** + * @brief Write data to TLS connection + * + * This function encrypts and writes application data to the TLS connection. + * It should: + * - Encrypt application data using the TLS stack + * - Write encrypted data to tls->sockfd + * - Handle partial writes and blocking/non-blocking behavior + * + * @param user_ctx User context pointer passed to esp_tls_register_stack() + * @param tls TLS context (handshake must be completed) + * @param data Application data to encrypt and send + * @param datalen Number of bytes to write + * + * @return + * - Positive value: Number of bytes written (should equal datalen if successful) + * - ESP_TLS_ERR_SSL_WANT_READ: Need to read data (renegotiation) + * - ESP_TLS_ERR_SSL_WANT_WRITE: Socket buffer full, try again later + * - Negative value: Error occurred + * + * @note This function is called by tls->write() callback, which is set during handshake. + * @note The function may write less than datalen if the socket buffer is full (non-blocking mode). + * The caller should retry with remaining data. + */ + ssize_t (*write)(void *user_ctx, esp_tls_t *tls, const char *data, size_t datalen); + + /** + * @brief Delete TLS connection and free resources + * + * This function cleans up the TLS connection and frees all resources. + * It should: + * - Close the TLS/SSL session + * - Free any stack-specific context stored in tls->priv_ctx or tls->priv_ssl + * - Free any allocated memory + * - Note: tls->sockfd is closed separately by ESP-TLS, don't close it here + * + * @param user_ctx User context pointer passed to esp_tls_register_stack() + * @param tls TLS context to clean up + * + * @note This function should be idempotent (safe to call multiple times). + * @note After this call, the tls context may be freed, so don't access it afterwards. + */ + void (*conn_delete)(void *user_ctx, esp_tls_t *tls); + + /** + * @brief Initialize network context + * + * This function initializes any network-related structures needed by the TLS stack. + * It should: + * - Initialize socket wrappers or network context structures + * - Set up any network-related state + * - This is typically called once per TLS context before create_ssl_handle + * + * @param user_ctx User context pointer passed to esp_tls_register_stack() + * @param tls TLS context (sockfd is already set) + * + * @note For some stacks, this may be a no-op if network initialization is handled elsewhere. + * @note This is called early in the connection setup, before create_ssl_handle. + */ + void (*net_init)(void *user_ctx, esp_tls_t *tls); + + /** + * @brief Get SSL context (stack-specific) + * + * This function returns the underlying SSL/TLS context object from the stack. + * This allows users to access stack-specific APIs directly if needed. + * + * @param user_ctx User context pointer passed to esp_tls_register_stack() + * @param tls TLS context + * + * @return Pointer to stack-specific SSL context (e.g., mbedtls_ssl_context*, SSL_CTX*, etc.) + * or NULL if not available + * + * @note The returned pointer is stack-specific and should be cast to the appropriate type + * by users who know which stack is registered. + * @note This is optional but recommended for advanced users who need stack-specific features. + */ + void *(*get_ssl_context)(void *user_ctx, esp_tls_t *tls); + + /** + * @brief Get bytes available for reading + * + * This function returns the number of decrypted application data bytes + * available to read without blocking. + * + * @param user_ctx User context pointer passed to esp_tls_register_stack() + * @param tls TLS context + * + * @return + * - Positive value: Number of bytes available to read + * - 0: No data available + * - Negative value: Error occurred + * + * @note This checks the TLS stack's internal buffer, not the socket buffer. + * @note Useful for non-blocking I/O to check if data is ready before calling read(). + */ + ssize_t (*get_bytes_avail)(void *user_ctx, esp_tls_t *tls); + + /** + * @brief Initialize global CA store + * + * This function initializes a global certificate authority (CA) store that can be + * used by all TLS connections. This is useful for applications that want to use + * the same CA certificates for multiple connections. + * + * @param user_ctx User context pointer passed to esp_tls_register_stack() + * + * @return + * - ESP_OK: CA store initialized successfully + * - ESP_ERR_NO_MEM: Memory allocation failed + * - Other error codes: Stack-specific errors + * + * @note This is called once at application startup if esp_tls_init_global_ca_store() is used. + * @note The global CA store is optional - individual connections can use their own CA certificates. + */ + esp_err_t (*init_global_ca_store)(void *user_ctx); + + /** + * @brief Set global CA store + * + * This function loads CA certificates into the global CA store. + * The certificates are used for server certificate verification in client connections. + * + * @param user_ctx User context pointer passed to esp_tls_register_stack() + * @param cacert_pem_buf CA certificate buffer in PEM format (NULL-terminated) + * @param cacert_pem_bytes Size of CA certificate buffer (including NULL terminator for PEM) + * + * @return + * - ESP_OK: CA certificates loaded successfully + * - ESP_ERR_INVALID_ARG: Invalid certificate format + * - ESP_ERR_NO_MEM: Memory allocation failed + * - Other error codes: Stack-specific errors + * + * @note The buffer should contain one or more PEM-formatted CA certificates. + * @note For PEM format, the buffer must be NULL-terminated. + * @note This can be called multiple times to add more certificates. + */ + esp_err_t (*set_global_ca_store)(void *user_ctx, const unsigned char *cacert_pem_buf, const unsigned int cacert_pem_bytes); + + /** + * @brief Get global CA store (stack-specific) + * + * This function returns the underlying CA store object from the stack. + * This allows users to access stack-specific CA store APIs directly if needed. + * + * @param user_ctx User context pointer passed to esp_tls_register_stack() + * + * @return Pointer to stack-specific CA store (e.g., mbedtls_x509_crt*, X509_STORE*, etc.) + * or NULL if not initialized + * + * @note The returned pointer is stack-specific and should be cast to the appropriate type. + * @note This is optional but useful for advanced users who need stack-specific features. + */ + void *(*get_global_ca_store)(void *user_ctx); + + /** + * @brief Free global CA store + * + * This function frees all resources associated with the global CA store. + * + * @param user_ctx User context pointer passed to esp_tls_register_stack() + * + * @note This should free all certificates and memory allocated for the CA store. + * @note After this call, the global CA store is no longer usable until reinitialized. + */ + void (*free_global_ca_store)(void *user_ctx); + + /** + * @brief Get list of supported ciphersuites + * + * This function returns a list of IANA ciphersuite identifiers supported by the stack. + * This is used by esp_tls_get_ciphersuites_list() API. + * + * @param user_ctx User context pointer passed to esp_tls_register_stack() + * + * @return Pointer to zero-terminated array of IANA ciphersuite identifiers (integers) + * The array must be terminated with 0. + * + * @note The returned pointer should point to static/const data (not dynamically allocated). + * @note Ciphersuite identifiers are IANA standard values (e.g., 0x0035 for TLS_RSA_WITH_AES_256_CBC_SHA). + * @note This list is used for validation when users specify custom ciphersuite lists. + */ + const int *(*get_ciphersuites_list)(void *user_ctx); + + /** + * @brief Get client session (optional, for session ticket support) + * + * This function retrieves the client session ticket/session data that can be reused + * for subsequent connections to the same server (RFC 5077). + * + * @param user_ctx User context pointer passed to esp_tls_register_stack() + * @param tls TLS context (handshake must be completed) + * + * @return Pointer to client session data (stack-specific type), or NULL if: + * - Session tickets are not supported + * - No session ticket was received + * - Error occurred + * + * @note The returned session can be stored and reused in future connections to the same server. + * @note This is only used if CONFIG_ESP_TLS_CLIENT_SESSION_TICKETS is enabled. + * @note The session data is stack-specific and opaque to ESP-TLS. + * @note Can be NULL if session tickets are not supported by the stack. + */ + void *(*get_client_session)(void *user_ctx, esp_tls_t *tls); + + /** + * @brief Free client session (optional, for session ticket support) + * + * This function frees resources associated with a client session retrieved by get_client_session(). + * + * @param user_ctx User context pointer passed to esp_tls_register_stack() + * @param client_session Client session to free (returned by get_client_session()) + * + * @note This should free all memory associated with the session. + * @note This is only used if CONFIG_ESP_TLS_CLIENT_SESSION_TICKETS is enabled. + * @note Can be NULL if session tickets are not supported by the stack. + */ + void (*free_client_session)(void *user_ctx, void *client_session); + + /** + * @brief Initialize server session ticket context (optional, for server session ticket support) + * + * This function initializes the server-side session ticket context for issuing + * and validating session tickets (RFC 5077). + * + * @param user_ctx User context pointer passed to esp_tls_register_stack() + * @param cfg Pointer to esp_tls_server_session_ticket_ctx_t structure + * Contains ticket encryption keys and configuration + * + * @return + * - ESP_OK: Session ticket context initialized successfully + * - ESP_ERR_INVALID_ARG: Invalid configuration + * - ESP_ERR_NO_MEM: Memory allocation failed + * - Other error codes: Stack-specific errors + * + * @note This is called when setting up server session tickets. + * @note The context should store encryption keys and other ticket-related state. + * @note This is only used if CONFIG_ESP_TLS_SERVER_SESSION_TICKETS is enabled. + * @note Can be NULL if server session tickets are not supported by the stack. + */ + esp_err_t (*server_session_ticket_ctx_init)(void *user_ctx, void *cfg); + + /** + * @brief Free server session ticket context (optional, for server session ticket support) + * + * This function frees resources associated with the server session ticket context. + * + * @param user_ctx User context pointer passed to esp_tls_register_stack() + * @param cfg Pointer to esp_tls_server_session_ticket_ctx_t structure to free + * + * @note This should free all memory and clear sensitive data (encryption keys). + * @note This is only used if CONFIG_ESP_TLS_SERVER_SESSION_TICKETS is enabled. + * @note Can be NULL if server session tickets are not supported by the stack. + */ + void (*server_session_ticket_ctx_free)(void *user_ctx, void *cfg); + + /** + * @brief Create server session (optional, server-side only) + * + * This function creates a complete TLS server session, performing initialization + * and handshake in a blocking manner. This is a convenience function that combines + * server_session_init() and server_session_continue_async(). + * + * @param user_ctx User context pointer passed to esp_tls_register_stack() + * @param cfg Server configuration (esp_tls_cfg_server_t) + * @param sockfd Socket file descriptor (already connected) + * @param tls TLS context (already allocated) + * + * @return + * - 1: Server session created successfully, handshake completed + * - 0: Should not happen (this is a blocking function) + * - ESP_TLS_ERR_SSL_WANT_READ: Need to read more data (should retry) + * - ESP_TLS_ERR_SSL_WANT_WRITE: Need to write more data (should retry) + * - Negative value: Error occurred + * + * @note This function should handle the complete handshake internally. + * @note For non-blocking operation, use server_session_init() + server_session_continue_async() instead. + * @note This is optional - if NULL, ESP-TLS will use server_session_init() + server_session_continue_async(). + */ + int (*server_session_create)(void *user_ctx, esp_tls_cfg_server_t *cfg, int sockfd, esp_tls_t *tls); + + /** + * @brief Initialize server session (optional, server-side only) + * + * This function initializes a TLS server session. It should: + * - Set up the TLS context for server mode + * - Configure server certificates and keys from cfg + * - Prepare for handshake (but don't perform it yet) + * - Set tls->read and tls->write callbacks + * + * @param user_ctx User context pointer passed to esp_tls_register_stack() + * @param cfg Server configuration (esp_tls_cfg_server_t) + * @param sockfd Socket file descriptor (already connected) + * @param tls TLS context (already allocated) + * + * @return + * - ESP_OK: Server session initialized successfully + * - ESP_ERR_INVALID_ARG: Invalid configuration + * - ESP_ERR_NO_MEM: Memory allocation failed + * - Other error codes: Stack-specific errors + * + * @note After this, call server_session_continue_async() to perform the handshake. + * @note This is optional - if NULL, server_session_create() must be implemented. + */ + esp_err_t (*server_session_init)(void *user_ctx, esp_tls_cfg_server_t *cfg, int sockfd, esp_tls_t *tls); + + /** + * @brief Continue async server session handshake (optional, server-side only) + * + * This function continues the TLS handshake for a server session initialized + * with server_session_init(). It should be called repeatedly until it returns 0. + * + * @param user_ctx User context pointer passed to esp_tls_register_stack() + * @param tls TLS context (initialized by server_session_init) + * + * @return + * - 0: Handshake completed successfully + * - ESP_TLS_ERR_SSL_WANT_READ: Need to read more data from client, call again later + * - ESP_TLS_ERR_SSL_WANT_WRITE: Need to write data to client, call again later + * - Negative value: Handshake failed (error code) + * + * @note This should be called in a loop until it returns 0 or a negative error. + * @note The ESP-TLS layer handles the looping and timeout management. + * @note This is optional - if NULL, server_session_create() must be implemented. + */ + int (*server_session_continue_async)(void *user_ctx, esp_tls_t *tls); + + /** + * @brief Delete server session (optional, server-side only) + * + * This function cleans up a server TLS session. It should: + * - Close the TLS session + * - Free any server-specific resources + * - Note: tls->sockfd is closed separately, don't close it here + * + * @param user_ctx User context pointer passed to esp_tls_register_stack() + * @param tls TLS context to clean up + * + * @note This is similar to conn_delete() but may have server-specific cleanup. + * @note If NULL, conn_delete() will be used instead. + */ + void (*server_session_delete)(void *user_ctx, esp_tls_t *tls); + + /** + * @brief Calculate SHA1 hash (optional, for esp_crypto_sha1 API) + * + * This function calculates the SHA1 hash of the input data. It is used by + * esp_crypto_sha1() API which is needed by components like esp_http_server + * for WebSocket key calculation. + * + * @param user_ctx User context pointer passed to esp_tls_register_stack() + * @param input Input data buffer + * @param ilen Length of input data + * @param output Output buffer for SHA1 hash (must be at least 20 bytes) + * + * @return + * - 0: Success + * - Negative value: Error occurred + * + * @note If NULL, esp_crypto_sha1() API calls will fail. This callback must be implemented + * if your application or any dependent component (e.g., esp_http_server for WebSocket) + * uses the esp_crypto_sha1() API. + */ + int (*crypto_sha1)(void *user_ctx, const unsigned char *input, size_t ilen, unsigned char output[20]); + + /** + * @brief Base64 encode data (optional, for esp_crypto_base64_encode API) + * + * This function performs Base64 encoding of the source data. It is used by + * esp_crypto_base64_encode() API which is needed by components like esp_http_server + * for WebSocket key encoding. + * + * @param user_ctx User context pointer passed to esp_tls_register_stack() + * @param dst Destination buffer for encoded data + * @param dlen Size of destination buffer + * @param olen Pointer to store the number of bytes written + * @param src Source data buffer + * @param slen Length of source data + * + * @return + * - 0: Success + * - -0x002A: Buffer too small + * - Other negative value: Error occurred + * + * @note If NULL, esp_crypto_base64_encode() API calls will fail. This callback must be + * implemented if your application or any dependent component (e.g., esp_http_server + * for WebSocket) uses the esp_crypto_base64_encode() API. + */ + int (*crypto_base64_encode)(void *user_ctx, unsigned char *dst, size_t dlen, size_t *olen, + const unsigned char *src, size_t slen); +} esp_tls_stack_ops_t; + +/** + * Compile-time check to detect structure layout changes. + * If this assertion fails, you MUST increment ESP_TLS_STACK_OPS_VERSION. + * + * Field count: 1 (version) + 23 (function pointers) = 24 + */ +ESP_STATIC_ASSERT(sizeof(esp_tls_stack_ops_t) == 24 * sizeof(void *), "esp_tls_stack_ops_t layout changed - update ESP_TLS_STACK_OPS_VERSION!"); + +/** + * @brief Register a custom TLS stack implementation + * + * This function allows an external component to register its TLS stack implementation. + * Once registered, all TLS connections created after this call will use the registered stack. + * + * @note This function must be called before creating any TLS connections (esp_tls_conn_new(), etc.). + * @note This function can only be called once. Subsequent calls will return ESP_ERR_INVALID_STATE. + * @note If CONFIG_ESP_TLS_CUSTOM_STACK is not enabled, this function will return ESP_ERR_NOT_SUPPORTED. + * @note All required function pointers in ops must be non-NULL. Optional functions can be NULL. + * @note The ops structure pointer is stored by reference, not copied. The structure must remain + * valid in memory for the entire duration of TLS operations (typically the lifetime of the + * application). Use a static or global variable, not a stack-allocated or dynamically + * allocated structure that may be freed. Using a `const` static structure allows the + * compiler to place it in flash (.rodata), saving ~80 bytes of RAM. + * + * @param ops Pointer to TLS stack operations vtable containing your implementation. + * Must point to a static/global structure (not on stack) as it's stored by reference. + * Required fields: + * - version (must be set to ESP_TLS_STACK_OPS_VERSION) + * - create_ssl_handle + * - handshake + * - read + * - write + * - conn_delete + * - net_init + * - get_ssl_context + * - get_bytes_avail + * - init_global_ca_store + * - set_global_ca_store + * - get_global_ca_store + * - free_global_ca_store + * - get_ciphersuites_list + * Optional functions (can be NULL if not supported): + * - get_client_session (for CONFIG_ESP_TLS_CLIENT_SESSION_TICKETS) + * - free_client_session (for CONFIG_ESP_TLS_CLIENT_SESSION_TICKETS) + * - server_session_ticket_ctx_init (for CONFIG_ESP_TLS_SERVER_SESSION_TICKETS) + * - server_session_ticket_ctx_free (for CONFIG_ESP_TLS_SERVER_SESSION_TICKETS) + * - server_session_create (server-side, can be NULL if server_session_init is provided) + * - server_session_init (server-side, can be NULL if server_session_create is provided) + * - server_session_continue_async (server-side, can be NULL if server_session_create is provided) + * - server_session_delete (server-side, can be NULL, conn_delete will be used) + * @param user_ctx User context pointer that will be passed to all callbacks as the first parameter. + * This allows C++ implementations to avoid singletons by passing instance pointers. + * Can be NULL if not needed. + * + * @return + * - ESP_OK: Stack registered successfully + * - ESP_ERR_INVALID_ARG: ops is NULL or any required function pointer is NULL + * - ESP_ERR_INVALID_VERSION: ops->version does not match ESP_TLS_STACK_OPS_VERSION + * - ESP_ERR_INVALID_STATE: A stack is already registered + * - ESP_ERR_NOT_SUPPORTED: CONFIG_ESP_TLS_CUSTOM_STACK is not enabled + * + * @code{.c} + * // Example usage: + * static const esp_tls_stack_ops_t my_tls_ops = { + * .version = ESP_TLS_STACK_OPS_VERSION, + * .create_ssl_handle = my_create_ssl_handle, + * .handshake = my_handshake, + * .read = my_read, + * .write = my_write, + * .conn_delete = my_conn_delete, + * .net_init = my_net_init, + * .get_ssl_context = my_get_ssl_context, + * .get_bytes_avail = my_get_bytes_avail, + * .init_global_ca_store = my_init_global_ca_store, + * .set_global_ca_store = my_set_global_ca_store, + * .get_global_ca_store = my_get_global_ca_store, + * .free_global_ca_store = my_free_global_ca_store, + * .get_ciphersuites_list = my_get_ciphersuites_list, + * // Optional functions... + * }; + * + * void app_main(void) { + * esp_err_t ret = esp_tls_register_stack(&my_tls_ops, NULL); + * if (ret != ESP_OK) { + * ESP_LOGE("APP", "Failed to register TLS stack: %s", esp_err_to_name(ret)); + * return; + * } + * // ... create TLS connections as usual ... + * } + * @endcode + */ +esp_err_t esp_tls_register_stack(const esp_tls_stack_ops_t *ops, void *user_ctx); + +/** + * @brief Unregister the custom TLS stack implementation + * + * This function removes the registered TLS stack, allowing a new stack to be registered. + * This is primarily useful for testing scenarios where you need to switch between + * different TLS stack implementations. + * + * @note This function should NOT be called while any TLS connections are active. + * Ensure all TLS connections are closed before calling this function. + * @note If CONFIG_ESP_TLS_CUSTOM_STACK is not enabled, this function will return ESP_ERR_NOT_SUPPORTED. + * + * @return + * - ESP_OK: Stack unregistered successfully + * - ESP_ERR_INVALID_STATE: No stack was registered + * - ESP_ERR_NOT_SUPPORTED: CONFIG_ESP_TLS_CUSTOM_STACK is not enabled + */ +esp_err_t esp_tls_unregister_stack(void); + +/** + * @brief Get the registered TLS stack operations + * + * @return Pointer to registered TLS stack operations, or NULL if no stack is registered + * or CONFIG_ESP_TLS_CUSTOM_STACK is not enabled + */ +const esp_tls_stack_ops_t *esp_tls_get_registered_stack(void); + +/** + * @brief Internal wrapper functions that call through the registered stack's vtable + * + * These functions provide a uniform interface to the registered TLS stack implementation. + * They forward calls to the appropriate function pointer in the registered esp_tls_stack_ops_t structure. + * + * @note These are internal functions used by ESP-TLS and should not be called directly by applications. + * @{ + */ + +/** @brief Create SSL handle for a TLS connection */ +esp_err_t esp_tls_custom_stack_create_ssl_handle(const char *hostname, size_t hostlen, const void *cfg, esp_tls_t *tls, void *server_params); + +/** @brief Perform TLS handshake */ +int esp_tls_custom_stack_handshake(esp_tls_t *tls, const esp_tls_cfg_t *cfg); + +/** @brief Read data from TLS connection */ +ssize_t esp_tls_custom_stack_read(esp_tls_t *tls, char *data, size_t datalen); + +/** @brief Write data to TLS connection */ +ssize_t esp_tls_custom_stack_write(esp_tls_t *tls, const char *data, size_t datalen); + +/** @brief Delete TLS connection and free resources */ +void esp_tls_custom_stack_conn_delete(esp_tls_t *tls); + +/** @brief Initialize network context for TLS connection */ +void esp_tls_custom_stack_net_init(esp_tls_t *tls); + +/** @brief Get SSL context (stack-specific) */ +void *esp_tls_custom_stack_get_ssl_context(esp_tls_t *tls); + +/** @brief Get number of bytes available for reading */ +ssize_t esp_tls_custom_stack_get_bytes_avail(esp_tls_t *tls); + +/** @brief Initialize global CA certificate store */ +esp_err_t esp_tls_custom_stack_init_global_ca_store(void); + +/** @brief Set global CA certificate store */ +esp_err_t esp_tls_custom_stack_set_global_ca_store(const unsigned char *cacert_pem_buf, const unsigned int cacert_pem_bytes); + +/** @brief Get global CA certificate store (stack-specific) */ +void *esp_tls_custom_stack_get_global_ca_store(void); + +/** @brief Free global CA certificate store */ +void esp_tls_custom_stack_free_global_ca_store(void); + +/** @brief Get list of supported ciphersuites */ +const int *esp_tls_custom_stack_get_ciphersuites_list(void); + +/** @brief Get client session ticket for reuse */ +void *esp_tls_custom_stack_get_client_session(esp_tls_t *tls); + +/** @brief Free client session ticket */ +void esp_tls_custom_stack_free_client_session(void *client_session); + +/** @brief Initialize server session ticket context */ +esp_err_t esp_tls_custom_stack_server_session_ticket_ctx_init(void *cfg); + +/** @brief Free server session ticket context */ +void esp_tls_custom_stack_server_session_ticket_ctx_free(void *cfg); + +/** @brief Create server session (blocking) */ +int esp_tls_custom_stack_server_session_create(esp_tls_cfg_server_t *cfg, int sockfd, esp_tls_t *tls); + +/** @brief Initialize server session (non-blocking) */ +esp_err_t esp_tls_custom_stack_server_session_init(esp_tls_cfg_server_t *cfg, int sockfd, esp_tls_t *tls); + +/** @brief Continue async server session handshake */ +int esp_tls_custom_stack_server_session_continue_async(esp_tls_t *tls); + +/** @brief Delete server session */ +void esp_tls_custom_stack_server_session_delete(esp_tls_t *tls); + +/** @brief Calculate SHA1 hash using registered stack's implementation */ +int esp_tls_custom_stack_crypto_sha1(const unsigned char *input, size_t ilen, unsigned char output[20]); + +/** @brief Base64 encode using registered stack's implementation */ +int esp_tls_custom_stack_crypto_base64_encode(unsigned char *dst, size_t dlen, size_t *olen, + const unsigned char *src, size_t slen); + +/** @} */ + +#ifdef __cplusplus +} +#endif diff --git a/components/esp-tls/esp_tls_error_capture.c b/components/esp-tls/esp_tls_error_capture.c index f5dbcf5dbe..ffb34002ce 100644 --- a/components/esp-tls/esp_tls_error_capture.c +++ b/components/esp-tls/esp_tls_error_capture.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2020-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -22,10 +22,10 @@ void esp_tls_internal_event_tracker_capture(esp_tls_error_handle_t h, uint32_t t if (type == ESP_TLS_ERR_TYPE_ESP) { storage->parent.last_error = code; } else if (type == ESP_TLS_ERR_TYPE_MBEDTLS || - type == ESP_TLS_ERR_TYPE_WOLFSSL) { + type == ESP_TLS_ERR_TYPE_CUSTOM_STACK) { storage->parent.esp_tls_error_code = code; } else if (type == ESP_TLS_ERR_TYPE_MBEDTLS_CERT_FLAGS || - type == ESP_TLS_ERR_TYPE_WOLFSSL_CERT_FLAGS) { + type == ESP_TLS_ERR_TYPE_CUSTOM_STACK_CERT_FLAGS) { storage->parent.esp_tls_flags = code; } else if (type == ESP_TLS_ERR_TYPE_SYSTEM) { storage->sock_errno = code; @@ -58,11 +58,11 @@ esp_err_t esp_tls_get_and_clear_error_type(esp_tls_error_handle_t h, esp_tls_err *code = storage->parent.last_error; storage->parent.last_error = 0; } else if (type == ESP_TLS_ERR_TYPE_MBEDTLS || - type == ESP_TLS_ERR_TYPE_WOLFSSL) { + type == ESP_TLS_ERR_TYPE_CUSTOM_STACK) { *code = storage->parent.esp_tls_error_code; storage->parent.esp_tls_error_code = 0; } else if (type == ESP_TLS_ERR_TYPE_MBEDTLS_CERT_FLAGS || - type == ESP_TLS_ERR_TYPE_WOLFSSL_CERT_FLAGS) { + type == ESP_TLS_ERR_TYPE_CUSTOM_STACK_CERT_FLAGS) { *code = storage->parent.esp_tls_flags; storage->parent.esp_tls_flags = 0; } else if (type == ESP_TLS_ERR_TYPE_SYSTEM) { diff --git a/components/esp-tls/esp_tls_errors.h b/components/esp-tls/esp_tls_errors.h index c9d196283f..47b73e5eae 100644 --- a/components/esp-tls/esp_tls_errors.h +++ b/components/esp-tls/esp_tls_errors.h @@ -11,9 +11,6 @@ #ifdef CONFIG_ESP_TLS_USING_MBEDTLS #include "mbedtls/error.h" #include "mbedtls/ssl.h" -#elif CONFIG_ESP_TLS_USING_WOLFSSL -#include "wolfssl/wolfcrypt/settings.h" -#include "wolfssl/ssl.h" #endif @@ -49,17 +46,6 @@ extern "C" { #define ESP_ERR_MBEDTLS_SSL_TICKET_SETUP_FAILED (ESP_ERR_ESP_TLS_BASE + 0x1C) /*!< mbedtls api returned failed */ #define ESP_ERR_MBEDTLS_SSL_READ_FAILED (ESP_ERR_ESP_TLS_BASE + 0x1D) /*!< mbedtls api returned failed */ -/* wolfssl specific error codes */ -#define ESP_ERR_WOLFSSL_SSL_SET_HOSTNAME_FAILED (ESP_ERR_ESP_TLS_BASE + 0x31) /*!< wolfSSL api returned error */ -#define ESP_ERR_WOLFSSL_SSL_CONF_ALPN_PROTOCOLS_FAILED (ESP_ERR_ESP_TLS_BASE + 0x32) /*!< wolfSSL api returned error */ -#define ESP_ERR_WOLFSSL_CERT_VERIFY_SETUP_FAILED (ESP_ERR_ESP_TLS_BASE + 0x33) /*!< wolfSSL api returned error */ -#define ESP_ERR_WOLFSSL_KEY_VERIFY_SETUP_FAILED (ESP_ERR_ESP_TLS_BASE + 0x34) /*!< wolfSSL api returned error */ -#define ESP_ERR_WOLFSSL_SSL_HANDSHAKE_FAILED (ESP_ERR_ESP_TLS_BASE + 0x35) /*!< wolfSSL api returned failed */ -#define ESP_ERR_WOLFSSL_CTX_SETUP_FAILED (ESP_ERR_ESP_TLS_BASE + 0x36) /*!< wolfSSL api returned failed */ -#define ESP_ERR_WOLFSSL_SSL_SETUP_FAILED (ESP_ERR_ESP_TLS_BASE + 0x37) /*!< wolfSSL api returned failed */ -#define ESP_ERR_WOLFSSL_SSL_WRITE_FAILED (ESP_ERR_ESP_TLS_BASE + 0x38) /*!< wolfSSL api returned failed */ - - /** * Definition of errors reported from IO API (potentially non-blocking) in case of error: * - esp_tls_conn_read() @@ -69,11 +55,12 @@ extern "C" { #define ESP_TLS_ERR_SSL_WANT_READ MBEDTLS_ERR_SSL_WANT_READ #define ESP_TLS_ERR_SSL_WANT_WRITE MBEDTLS_ERR_SSL_WANT_WRITE #define ESP_TLS_ERR_SSL_TIMEOUT MBEDTLS_ERR_SSL_TIMEOUT -#elif CONFIG_ESP_TLS_USING_WOLFSSL /* CONFIG_ESP_TLS_USING_MBEDTLS */ -#define ESP_TLS_ERR_SSL_WANT_READ -0x6900 -#define ESP_TLS_ERR_SSL_WANT_WRITE -0x6880 -#define ESP_TLS_ERR_SSL_TIMEOUT WOLFSSL_CBIO_ERR_TIMEOUT -#endif /*CONFIG_ESP_TLS_USING_WOLFSSL */ +#elif CONFIG_ESP_TLS_CUSTOM_STACK +/* For custom stacks, use standard error codes that match mbedTLS conventions */ +#define ESP_TLS_ERR_SSL_WANT_READ -0x6900 /* SSL wants to read more data */ +#define ESP_TLS_ERR_SSL_WANT_WRITE -0x6880 /* SSL wants to write more data */ +#define ESP_TLS_ERR_SSL_TIMEOUT -0x6800 /* SSL operation timed out */ +#endif /** * Definition of different types/sources of error codes reported @@ -81,13 +68,13 @@ extern "C" { */ typedef enum { ESP_TLS_ERR_TYPE_UNKNOWN = 0, - ESP_TLS_ERR_TYPE_SYSTEM, /*!< System error -- errno */ - ESP_TLS_ERR_TYPE_MBEDTLS, /*!< Error code from mbedTLS library */ - ESP_TLS_ERR_TYPE_MBEDTLS_CERT_FLAGS, /*!< Certificate flags defined in mbedTLS */ - ESP_TLS_ERR_TYPE_ESP, /*!< ESP-IDF error type -- esp_err_t */ - ESP_TLS_ERR_TYPE_WOLFSSL, /*!< Error code from wolfSSL library */ - ESP_TLS_ERR_TYPE_WOLFSSL_CERT_FLAGS, /*!< Certificate flags defined in wolfSSL */ - ESP_TLS_ERR_TYPE_MAX, /*!< Last err type -- invalid entry */ + ESP_TLS_ERR_TYPE_SYSTEM, /*!< System error -- errno */ + ESP_TLS_ERR_TYPE_MBEDTLS, /*!< Error code from mbedTLS library */ + ESP_TLS_ERR_TYPE_MBEDTLS_CERT_FLAGS, /*!< Certificate flags defined in mbedTLS */ + ESP_TLS_ERR_TYPE_ESP, /*!< ESP-IDF error type -- esp_err_t */ + ESP_TLS_ERR_TYPE_CUSTOM_STACK, /*!< Error code from custom TLS stack */ + ESP_TLS_ERR_TYPE_CUSTOM_STACK_CERT_FLAGS, /*!< Certificate flags from custom TLS stack */ + ESP_TLS_ERR_TYPE_MAX, /*!< Last err type -- invalid entry */ } esp_tls_error_type_t; typedef struct esp_tls_last_error* esp_tls_error_handle_t; diff --git a/components/esp-tls/esp_tls_wolfssl.c b/components/esp-tls/esp_tls_wolfssl.c deleted file mode 100644 index 6096d717e4..0000000000 --- a/components/esp-tls/esp_tls_wolfssl.c +++ /dev/null @@ -1,642 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2019-2023 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include -#include -#include -#include - -#include -#include -#include - -#include -#include "esp_tls_wolfssl.h" -#include "esp_tls_error_capture_internal.h" -#include -#include "esp_log.h" - -static unsigned char *global_cacert = NULL; -static unsigned int global_cacert_pem_bytes = 0; -static const char *TAG = "esp-tls-wolfssl"; - -/* Prototypes for the static functions */ -static esp_err_t set_client_config(const char *hostname, size_t hostlen, esp_tls_cfg_t *cfg, esp_tls_t *tls); - -#if defined(CONFIG_ESP_TLS_PSK_VERIFICATION) -#include "freertos/semphr.h" -static SemaphoreHandle_t tls_conn_lock; -static inline unsigned int esp_wolfssl_psk_client_cb(WOLFSSL* ssl, const char* hint, char* identity, - unsigned int id_max_len, unsigned char* key,unsigned int key_max_len); -static esp_err_t esp_wolfssl_set_cipher_list(WOLFSSL_CTX *ctx); -#ifdef WOLFSSL_TLS13 -#define PSK_MAX_ID_LEN 128 -#else -#define PSK_MAX_ID_LEN 64 -#endif -#define PSK_MAX_KEY_LEN 64 - -static char psk_id_str[PSK_MAX_ID_LEN]; -static uint8_t psk_key_array[PSK_MAX_KEY_LEN]; -static uint8_t psk_key_max_len = 0; -#endif /* CONFIG_ESP_TLS_PSK_VERIFICATION */ - -static esp_err_t set_server_config(esp_tls_cfg_server_t *cfg, esp_tls_t *tls); - - -/* This function shall return the error message when appropriate log level has been set otherwise this function shall do nothing */ -static void wolfssl_print_error_msg(int error) -{ -#if (CONFIG_LOG_DEFAULT_LEVEL_DEBUG || CONFIG_LOG_DEFAULT_LEVEL_VERBOSE) - static char error_buf[100]; - ESP_LOGE(TAG, "(%d) : %s", error, ERR_error_string(error, error_buf)); -#endif -} - -typedef enum x509_file_type { - FILE_TYPE_CA_CERT = 0, /* CA certificate to authenticate entity at other end */ - FILE_TYPE_SELF_CERT, /* Self certificate of the entity */ - FILE_TYPE_SELF_KEY, /* Private key in the self cert-key pair */ -} x509_file_type_t; - -/* Error type conversion utility so that esp-tls read/write API to return negative number on error */ -static inline ssize_t esp_tls_convert_wolfssl_err_to_ssize(int wolfssl_error) -{ - switch (wolfssl_error) { - case WOLFSSL_ERROR_WANT_READ: - return ESP_TLS_ERR_SSL_WANT_READ; - case WOLFSSL_ERROR_WANT_WRITE: - return ESP_TLS_ERR_SSL_WANT_WRITE; - default: - // Make sure we return a negative number - return wolfssl_error>0 ? -wolfssl_error: wolfssl_error; - } -} - -/* Checks whether the certificate provided is in pem format or not */ -static esp_err_t esp_load_wolfssl_verify_buffer(esp_tls_t *tls, const unsigned char *cert_buf, unsigned int cert_len, x509_file_type_t type, int *err_ret) -{ - int wolf_fileformat = WOLFSSL_FILETYPE_DEFAULT; - if (type == FILE_TYPE_SELF_KEY) { - if (cert_buf[cert_len - 1] == '\0' && strstr( (const char *) cert_buf, "-----BEGIN " )) { - wolf_fileformat = WOLFSSL_FILETYPE_PEM; - } else { - wolf_fileformat = WOLFSSL_FILETYPE_ASN1; - } - if ((*err_ret = wolfSSL_CTX_use_PrivateKey_buffer( (WOLFSSL_CTX *)tls->priv_ctx, cert_buf, cert_len, wolf_fileformat)) == WOLFSSL_SUCCESS) { - return ESP_OK; - } - return ESP_FAIL; - } else { - if (cert_buf[cert_len - 1] == '\0' && strstr( (const char *) cert_buf, "-----BEGIN CERTIFICATE-----" )) { - wolf_fileformat = WOLFSSL_FILETYPE_PEM; - } else { - wolf_fileformat = WOLFSSL_FILETYPE_ASN1; - } - if (type == FILE_TYPE_SELF_CERT) { - if ((*err_ret = wolfSSL_CTX_use_certificate_chain_buffer_format( (WOLFSSL_CTX *)tls->priv_ctx, cert_buf, cert_len, wolf_fileformat)) == WOLFSSL_SUCCESS) { - return ESP_OK; - } - return ESP_FAIL; - } else if (type == FILE_TYPE_CA_CERT) { - if ((*err_ret = wolfSSL_CTX_load_verify_buffer( (WOLFSSL_CTX *)tls->priv_ctx, cert_buf, cert_len, wolf_fileformat)) == WOLFSSL_SUCCESS) { - return ESP_OK; - } - return ESP_FAIL; - } else { - /* Wrong file type provided */ - return ESP_FAIL; - } - } -} - -void *esp_wolfssl_get_ssl_context(esp_tls_t *tls) -{ - if (tls == NULL) { - ESP_LOGE(TAG, "Invalid arguments"); - return NULL; - } - return (void*)tls->priv_ssl; -} - -esp_err_t esp_create_wolfssl_handle(const char *hostname, size_t hostlen, const void *cfg, esp_tls_t *tls, void *server_params) -{ -#ifdef CONFIG_ESP_DEBUG_WOLFSSL - wolfSSL_Debugging_ON(); -#endif - - assert(cfg != NULL); - assert(tls != NULL); - - esp_err_t esp_ret = ESP_FAIL; - int ret; - - ret = wolfSSL_Init(); - if (ret != WOLFSSL_SUCCESS) { - ESP_LOGE(TAG, "Init wolfSSL failed: 0x%04X", ret); - wolfssl_print_error_msg(ret); - int err = wolfSSL_get_error( (WOLFSSL *)tls->priv_ssl, ret); - ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ESP_TLS_ERR_TYPE_WOLFSSL, err); - goto exit; - } - - if (tls->role == ESP_TLS_CLIENT) { - esp_ret = set_client_config(hostname, hostlen, (esp_tls_cfg_t *)cfg, tls); - if (esp_ret != ESP_OK) { - ESP_LOGE(TAG, "Failed to set client configurations, [0x%04X] (%s)", esp_ret, esp_err_to_name(esp_ret)); - goto exit; - } - } else if (tls->role == ESP_TLS_SERVER) { - esp_ret = set_server_config((esp_tls_cfg_server_t *) cfg, tls); - if (esp_ret != ESP_OK) { - ESP_LOGE(TAG, "Failed to set server configurations, [0x%04X] (%s)", esp_ret, esp_err_to_name(esp_ret)); - goto exit; - } - } - else { - ESP_LOGE(TAG, "tls->role is not valid"); - goto exit; - } - - return ESP_OK; -exit: - esp_wolfssl_cleanup(tls); - return esp_ret; -} - -static esp_err_t set_client_config(const char *hostname, size_t hostlen, esp_tls_cfg_t *cfg, esp_tls_t *tls) -{ - int ret = WOLFSSL_FAILURE; - -#ifdef WOLFSSL_TLS13 - tls->priv_ctx = (void *)wolfSSL_CTX_new(wolfTLSv1_3_client_method()); -#else - tls->priv_ctx = (void *)wolfSSL_CTX_new(wolfTLSv1_2_client_method()); -#endif - - if (!tls->priv_ctx) { - ESP_LOGE(TAG, "Set wolfSSL ctx failed"); - ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ESP_TLS_ERR_TYPE_WOLFSSL, ret); - return ESP_ERR_WOLFSSL_CTX_SETUP_FAILED; - } - - if (cfg->crt_bundle_attach != NULL) { - ESP_LOGE(TAG,"use_crt_bundle not supported in wolfssl"); - return ESP_FAIL; - } - - if (cfg->use_global_ca_store == true) { - if ((esp_load_wolfssl_verify_buffer(tls, global_cacert, global_cacert_pem_bytes, FILE_TYPE_CA_CERT, &ret)) != ESP_OK) { - int err = wolfSSL_get_error( (WOLFSSL *)tls->priv_ssl, ret); - ESP_LOGE(TAG, "Error in loading certificate verify buffer, returned %d, error code: %d", ret, err); - wolfssl_print_error_msg(err); - return ESP_ERR_WOLFSSL_CERT_VERIFY_SETUP_FAILED; - } - wolfSSL_CTX_set_verify( (WOLFSSL_CTX *)tls->priv_ctx, WOLFSSL_VERIFY_PEER, NULL); - } else if (cfg->cacert_buf != NULL) { - if ((esp_load_wolfssl_verify_buffer(tls, cfg->cacert_buf, cfg->cacert_bytes, FILE_TYPE_CA_CERT, &ret)) != ESP_OK) { - int err = wolfSSL_get_error( (WOLFSSL *)tls->priv_ssl, ret); - ESP_LOGE(TAG, "Error in loading certificate verify buffer, returned %d, error code: %d", ret, err); - wolfssl_print_error_msg(err); - return ESP_ERR_WOLFSSL_CERT_VERIFY_SETUP_FAILED; - } - wolfSSL_CTX_set_verify( (WOLFSSL_CTX *)tls->priv_ctx, WOLFSSL_VERIFY_PEER, NULL); - } else if (cfg->psk_hint_key) { -#if defined(CONFIG_ESP_TLS_PSK_VERIFICATION) - /*** PSK encryption mode is configured only if no certificate supplied and psk pointer not null ***/ - if(cfg->psk_hint_key->key == NULL || cfg->psk_hint_key->hint == NULL || cfg->psk_hint_key->key_size <= 0) { - ESP_LOGE(TAG, "Please provide appropriate key, keysize and hint to use PSK"); - return ESP_FAIL; - } - /* mutex is given back when call back function executes or in case of failure (at cleanup) */ - if ((xSemaphoreTake(tls_conn_lock, 1000/portTICK_PERIOD_MS) != pdTRUE)) { - ESP_LOGE(TAG, "tls_conn_lock could not be obtained in specified time"); - return -1; - } - ESP_LOGI(TAG, "setting psk configurations"); - if((cfg->psk_hint_key->key_size > PSK_MAX_KEY_LEN) || (strlen(cfg->psk_hint_key->hint) > PSK_MAX_ID_LEN)) { - ESP_LOGE(TAG, "psk key length should be <= %d and identity hint length should be <= %d", PSK_MAX_KEY_LEN, PSK_MAX_ID_LEN); - return ESP_ERR_INVALID_ARG; - } - psk_key_max_len = cfg->psk_hint_key->key_size; - memset(psk_key_array, 0, sizeof(psk_key_array)); - memset(psk_id_str, 0, sizeof(psk_id_str)); - memcpy(psk_key_array, cfg->psk_hint_key->key, psk_key_max_len); - memcpy(psk_id_str, cfg->psk_hint_key->hint, strlen(cfg->psk_hint_key->hint)); - wolfSSL_CTX_set_psk_client_callback( (WOLFSSL_CTX *)tls->priv_ctx, esp_wolfssl_psk_client_cb); - if(esp_wolfssl_set_cipher_list( (WOLFSSL_CTX *)tls->priv_ctx) != ESP_OK) { - ESP_LOGE(TAG, "error in setting cipher-list"); - return ESP_FAIL; - } -#else - ESP_LOGE(TAG, "psk_hint_key configured but not enabled in menuconfig: Please enable ESP_TLS_PSK_VERIFICATION option"); - return ESP_ERR_INVALID_STATE; -#endif - } else { -#ifdef CONFIG_ESP_TLS_SKIP_SERVER_CERT_VERIFY - wolfSSL_CTX_set_verify( (WOLFSSL_CTX *)tls->priv_ctx, WOLFSSL_VERIFY_NONE, NULL); -#else - ESP_LOGE(TAG, "No server verification option set in esp_tls_cfg_t structure. Check esp_tls API reference"); - return ESP_ERR_WOLFSSL_SSL_SETUP_FAILED; -#endif - } - - if (cfg->clientcert_buf != NULL && cfg->clientkey_buf != NULL) { - if ((esp_load_wolfssl_verify_buffer(tls,cfg->clientcert_buf, cfg->clientcert_bytes, FILE_TYPE_SELF_CERT, &ret)) != ESP_OK) { - int err = wolfSSL_get_error( (WOLFSSL *)tls->priv_ssl, ret); - ESP_LOGE(TAG, "Error in loading certificate verify buffer, returned %d, error code: %d", ret, err); - wolfssl_print_error_msg(err); - return ESP_ERR_WOLFSSL_CERT_VERIFY_SETUP_FAILED; - } - if ((esp_load_wolfssl_verify_buffer(tls,cfg->clientkey_buf, cfg->clientkey_bytes, FILE_TYPE_SELF_KEY, &ret)) != ESP_OK) { - int err = wolfSSL_get_error( (WOLFSSL *)tls->priv_ssl, ret); - ESP_LOGE(TAG, "Error in loading private key verify buffer, returned %d, error code: %d", ret, err); - wolfssl_print_error_msg(err); - return ESP_ERR_WOLFSSL_CERT_VERIFY_SETUP_FAILED; - } - } else if (cfg->clientcert_buf != NULL || cfg->clientkey_buf != NULL) { - ESP_LOGE(TAG, "You have to provide both clientcert_buf and clientkey_buf for mutual authentication"); - return ESP_FAIL; - } - - tls->priv_ssl =(void *)wolfSSL_new( (WOLFSSL_CTX *)tls->priv_ctx); - if (!tls->priv_ssl) { - ESP_LOGE(TAG, "Create wolfSSL failed"); - int err = wolfSSL_get_error( (WOLFSSL *)tls->priv_ssl, ret); - ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ESP_TLS_ERR_TYPE_WOLFSSL, err); - return ESP_ERR_WOLFSSL_SSL_SETUP_FAILED; - } - - if (!cfg->skip_common_name) { - char *use_host = NULL; - if (cfg->common_name != NULL) { - use_host = strdup(cfg->common_name); - } else { - use_host = strndup(hostname, hostlen); - } - if (use_host == NULL) { - return ESP_ERR_NO_MEM; - } - /* Hostname set here should match CN in server certificate */ - if ((ret = (wolfSSL_check_domain_name( (WOLFSSL *)tls->priv_ssl, use_host))) != WOLFSSL_SUCCESS) { - int err = wolfSSL_get_error( (WOLFSSL *)tls->priv_ssl, ret); - ESP_LOGE(TAG, "wolfSSL_check_domain_name returned %d, error code: %d", ret, err); - ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ESP_TLS_ERR_TYPE_WOLFSSL, err); - free(use_host); - return ESP_ERR_WOLFSSL_SSL_SET_HOSTNAME_FAILED; - } - /* Mimic the semantics of mbedtls_ssl_set_hostname() */ - if ((ret = wolfSSL_CTX_UseSNI(tls->priv_ctx, WOLFSSL_SNI_HOST_NAME, use_host, strlen(use_host))) != WOLFSSL_SUCCESS) { - ESP_LOGE(TAG, "wolfSSL_CTX_UseSNI failed, returned %d", ret); - return ESP_ERR_WOLFSSL_SSL_SET_HOSTNAME_FAILED; - } - free(use_host); - } - - if (cfg->alpn_protos) { -#ifdef CONFIG_WOLFSSL_HAVE_ALPN - char **alpn_list = (char **)cfg->alpn_protos; - for (; *alpn_list != NULL; alpn_list ++) { - ESP_LOGD(TAG, "alpn protocol is %s", *alpn_list); - if ((ret = wolfSSL_UseALPN( (WOLFSSL *)tls->priv_ssl, *alpn_list, strlen(*alpn_list), WOLFSSL_ALPN_FAILED_ON_MISMATCH)) != WOLFSSL_SUCCESS) { - int err = wolfSSL_get_error( (WOLFSSL *)tls->priv_ssl, ret); - ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ESP_TLS_ERR_TYPE_WOLFSSL, err); - ESP_LOGE(TAG, "wolfSSL UseALPN failed, returned %d, error code: %d", ret, err); - wolfssl_print_error_msg(err); - return ESP_ERR_WOLFSSL_SSL_CONF_ALPN_PROTOCOLS_FAILED; - } - } -#else - ESP_LOGE(TAG, "CONFIG_WOLFSSL_HAVE_ALPN not enabled in menuconfig"); - return ESP_FAIL; -#endif /* CONFIG_WOLFSSL_HAVE_ALPN */ - } - -#ifdef CONFIG_WOLFSSL_HAVE_OCSP - int ocsp_options = 0; -#ifdef ESP_TLS_OCSP_CHECKALL - ocsp_options |= WOLFSSL_OCSP_CHECKALL; -#endif - /* enable OCSP certificate status check for this TLS context */ - if ((ret = wolfSSL_CTX_EnableOCSP((WOLFSSL_CTX *)tls->priv_ctx, ocsp_options)) != WOLFSSL_SUCCESS) { - ESP_LOGE(TAG, "wolfSSL_CTX_EnableOCSP failed, returned %d", ret); - return ESP_ERR_WOLFSSL_CTX_SETUP_FAILED; - } - /* enable OCSP stapling for this TLS context */ - if ((ret = wolfSSL_CTX_EnableOCSPStapling((WOLFSSL_CTX *)tls->priv_ctx )) != WOLFSSL_SUCCESS) { - ESP_LOGE(TAG, "wolfSSL_CTX_EnableOCSPStapling failed, returned %d", ret); - return ESP_ERR_WOLFSSL_CTX_SETUP_FAILED; - } - /* set option to use OCSP v1 stapling with nounce extension */ - if ((ret = wolfSSL_UseOCSPStapling((WOLFSSL *)tls->priv_ssl, WOLFSSL_CSR_OCSP, WOLFSSL_CSR_OCSP_USE_NONCE)) != WOLFSSL_SUCCESS) { - ESP_LOGE(TAG, "wolfSSL_UseOCSPStapling failed, returned %d", ret); - return ESP_ERR_WOLFSSL_SSL_SETUP_FAILED; - } -#endif /* CONFIG_WOLFSSL_HAVE_OCSP */ - - wolfSSL_set_fd((WOLFSSL *)tls->priv_ssl, tls->sockfd); - return ESP_OK; -} - -static esp_err_t set_server_config(esp_tls_cfg_server_t *cfg, esp_tls_t *tls) -{ - int ret = WOLFSSL_FAILURE; - -#ifdef WOLFSSL_TLS13 - tls->priv_ctx = (void *)wolfSSL_CTX_new(wolfTLSv1_3_server_method()); -#else - tls->priv_ctx = (void *)wolfSSL_CTX_new(wolfTLSv1_2_server_method()); -#endif - - if (!tls->priv_ctx) { - ESP_LOGE(TAG, "Set wolfSSL ctx failed"); - return ESP_ERR_WOLFSSL_CTX_SETUP_FAILED; - } - - if (cfg->cacert_buf != NULL) { - if ((esp_load_wolfssl_verify_buffer(tls,cfg->cacert_buf, cfg->cacert_bytes, FILE_TYPE_CA_CERT, &ret)) != ESP_OK) { - int err = wolfSSL_get_error( (WOLFSSL *)tls->priv_ssl, ret); - ESP_LOGE(TAG, "Error in loading certificate verify buffer, returned %d, error code: %d", ret, err); - wolfssl_print_error_msg(err); - return ESP_ERR_WOLFSSL_CERT_VERIFY_SETUP_FAILED; - } - wolfSSL_CTX_set_verify( (WOLFSSL_CTX *)tls->priv_ctx, WOLFSSL_VERIFY_PEER | WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL); - ESP_LOGD(TAG," Verify Client for Mutual Auth"); - } else { - ESP_LOGD(TAG," Not verifying Client "); - wolfSSL_CTX_set_verify( (WOLFSSL_CTX *)tls->priv_ctx, WOLFSSL_VERIFY_NONE, NULL); - } - - if (cfg->servercert_buf != NULL && cfg->serverkey_buf != NULL) { - if ((esp_load_wolfssl_verify_buffer(tls,cfg->servercert_buf, cfg->servercert_bytes, FILE_TYPE_SELF_CERT, &ret)) != ESP_OK) { - int err = wolfSSL_get_error( (WOLFSSL *)tls->priv_ssl, ret); - ESP_LOGE(TAG, "Error in loading certificate verify buffer, returned %d, error code: %d", ret, err); - wolfssl_print_error_msg(err); - return ESP_ERR_WOLFSSL_CERT_VERIFY_SETUP_FAILED; - } - if ((esp_load_wolfssl_verify_buffer(tls,cfg->serverkey_buf, cfg->serverkey_bytes, FILE_TYPE_SELF_KEY, &ret)) != ESP_OK) { - int err = wolfSSL_get_error( (WOLFSSL *)tls->priv_ssl, ret); - ESP_LOGE(TAG, "Error in loading private key verify buffer, returned %d, error code: %d", ret, err); - wolfssl_print_error_msg(err); - return ESP_ERR_WOLFSSL_CERT_VERIFY_SETUP_FAILED; - } - } else { - ESP_LOGE(TAG, "You have to provide both servercert_buf and serverkey_buf for https_server"); - return ESP_FAIL; - } - - tls->priv_ssl =(void *)wolfSSL_new( (WOLFSSL_CTX *)tls->priv_ctx); - if (!tls->priv_ssl) { - ESP_LOGE(TAG, "Create wolfSSL failed"); - return ESP_ERR_WOLFSSL_SSL_SETUP_FAILED; - } - - wolfSSL_set_fd((WOLFSSL *)tls->priv_ssl, tls->sockfd); - return ESP_OK; -} - -int esp_wolfssl_handshake(esp_tls_t *tls, const esp_tls_cfg_t *cfg) -{ - int ret; - ret = wolfSSL_connect( (WOLFSSL *)tls->priv_ssl); - if (ret == WOLFSSL_SUCCESS) { - tls->conn_state = ESP_TLS_DONE; - return 1; - } else { - int err = wolfSSL_get_error( (WOLFSSL *)tls->priv_ssl, ret); - if (err != WOLFSSL_ERROR_WANT_READ && err != WOLFSSL_ERROR_WANT_WRITE) { - ESP_LOGE(TAG, "wolfSSL_connect returned %d, error code: %d", ret, err); - wolfssl_print_error_msg(err); - ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ESP_TLS_ERR_TYPE_WOLFSSL, err); - ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ESP_TLS_ERR_TYPE_ESP, ESP_ERR_WOLFSSL_SSL_HANDSHAKE_FAILED); - if (cfg->crt_bundle_attach != NULL || cfg->cacert_buf != NULL || cfg->use_global_ca_store == true) { - /* This is to check whether handshake failed due to invalid certificate*/ - esp_wolfssl_verify_certificate(tls); - } - tls->conn_state = ESP_TLS_FAIL; - return -1; - } - /* Irrespective of blocking or non-blocking I/O, we return on getting wolfSSL_want_read - or wolfSSL_want_write during handshake */ - return 0; - } -} - -ssize_t esp_wolfssl_read(esp_tls_t *tls, char *data, size_t datalen) -{ - ssize_t ret = wolfSSL_read( (WOLFSSL *)tls->priv_ssl, (unsigned char *)data, datalen); - if (ret < 0) { - int err = wolfSSL_get_error( (WOLFSSL *)tls->priv_ssl, ret); - /* peer sent close notify */ - if (err == WOLFSSL_ERROR_ZERO_RETURN) { - return 0; - } - - if (ret != WOLFSSL_ERROR_WANT_READ && ret != WOLFSSL_ERROR_WANT_WRITE) { - ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ESP_TLS_ERR_TYPE_WOLFSSL, -ret); - ESP_LOGE(TAG, "read error :%d:", ret); - wolfssl_print_error_msg(ret); - } - return esp_tls_convert_wolfssl_err_to_ssize(ret); - } - return ret; -} - -ssize_t esp_wolfssl_write(esp_tls_t *tls, const char *data, size_t datalen) -{ - ssize_t ret = wolfSSL_write( (WOLFSSL *)tls->priv_ssl, (unsigned char *) data, datalen); - if (ret <= 0) { - int err = wolfSSL_get_error( (WOLFSSL *)tls->priv_ssl, ret); - if (err != WOLFSSL_ERROR_WANT_READ && err != WOLFSSL_ERROR_WANT_WRITE) { - ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ESP_TLS_ERR_TYPE_WOLFSSL, -err); - ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ESP_TLS_ERR_TYPE_ESP, ESP_ERR_WOLFSSL_SSL_WRITE_FAILED); - ESP_LOGE(TAG, "write error :%d:", err); - wolfssl_print_error_msg(err); - } - return esp_tls_convert_wolfssl_err_to_ssize(ret); - } - return ret; -} - -void esp_wolfssl_verify_certificate(esp_tls_t *tls) -{ - int flags; - if ((flags = wolfSSL_get_verify_result( (WOLFSSL *)tls->priv_ssl)) != X509_V_OK) { - ESP_LOGE(TAG, "Failed to verify peer certificate , returned %d", flags); - ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ESP_TLS_ERR_TYPE_WOLFSSL_CERT_FLAGS, flags); - } else { - ESP_LOGI(TAG, "Certificate verified."); - } -} - -ssize_t esp_wolfssl_get_bytes_avail(esp_tls_t *tls) -{ - if (!tls) { - ESP_LOGE(TAG, "empty arg passed to esp_tls_get_bytes_avail()"); - return ESP_FAIL; - } - return wolfSSL_pending( (WOLFSSL *)tls->priv_ssl); -} - -void esp_wolfssl_conn_delete(esp_tls_t *tls) -{ - if (tls != NULL) { - esp_wolfssl_cleanup(tls); - } -} - -void esp_wolfssl_cleanup(esp_tls_t *tls) -{ - if (!tls) { - return; - } -#ifdef CONFIG_ESP_TLS_PSK_VERIFICATION - xSemaphoreGive(tls_conn_lock); -#endif /* CONFIG_ESP_TLS_PSK_VERIFICATION */ - wolfSSL_shutdown( (WOLFSSL *)tls->priv_ssl); - wolfSSL_free( (WOLFSSL *)tls->priv_ssl); - tls->priv_ssl = NULL; - wolfSSL_CTX_free( (WOLFSSL_CTX *)tls->priv_ctx); - tls->priv_ctx = NULL; - wolfSSL_Cleanup(); -} - -/** - * @brief Create TLS/SSL server session - */ -int esp_wolfssl_server_session_create(esp_tls_cfg_server_t *cfg, int sockfd, esp_tls_t *tls) -{ - if (tls == NULL || cfg == NULL) { - return -1; - } - tls->role = ESP_TLS_SERVER; - tls->sockfd = sockfd; - esp_tls_server_params_t server_params = {}; - server_params.set_server_cfg = &set_server_config; - esp_err_t esp_ret = esp_create_wolfssl_handle(NULL, 0, cfg, tls, &server_params); - if (esp_ret != ESP_OK) { - ESP_LOGE(TAG, "create_ssl_handle failed, [0x%04X] (%s)", esp_ret, esp_err_to_name(esp_ret)); - ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ESP_TLS_ERR_TYPE_ESP, esp_ret); - tls->conn_state = ESP_TLS_FAIL; - return -1; - } - tls->read = esp_wolfssl_read; - tls->write = esp_wolfssl_write; - int ret; - while ((ret = wolfSSL_accept((WOLFSSL *)tls->priv_ssl)) != WOLFSSL_SUCCESS) { - int err = wolfSSL_get_error((WOLFSSL *)tls->priv_ssl, ret); - if (err != WOLFSSL_ERROR_WANT_READ && ret != WOLFSSL_ERROR_WANT_WRITE) { - ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ESP_TLS_ERR_TYPE_WOLFSSL, err); - ESP_LOGE(TAG, "wolfSSL_accept returned %d, error code: %d", ret, err); - wolfssl_print_error_msg(err); - tls->conn_state = ESP_TLS_FAIL; - return -1; - } - } - return 0; -} - -/** - * @brief Close the server side TLS/SSL connection and free any allocated resources. - */ -void esp_wolfssl_server_session_delete(esp_tls_t *tls) -{ - if (tls != NULL) { - esp_wolfssl_cleanup(tls); - esp_tls_internal_event_tracker_destroy(tls->error_handle); - free(tls); - } -} - -esp_err_t esp_wolfssl_init_global_ca_store(void) -{ - /* This function is just to provide consistency between function calls of esp_tls.h and wolfssl */ - return ESP_OK; -} - -esp_err_t esp_wolfssl_set_global_ca_store(const unsigned char *cacert_pem_buf, const unsigned int cacert_pem_bytes) -{ - if (cacert_pem_buf == NULL) { - ESP_LOGE(TAG, "cacert_pem_buf is null"); - return ESP_ERR_INVALID_ARG; - } - if (global_cacert != NULL) { - esp_wolfssl_free_global_ca_store(); - } - - global_cacert = (unsigned char *)strndup((const char *)cacert_pem_buf, cacert_pem_bytes); - if (!global_cacert) { - return ESP_FAIL; - } - - global_cacert_pem_bytes = cacert_pem_bytes; - - return ESP_OK; -} - -void esp_wolfssl_free_global_ca_store(void) -{ - if (global_cacert) { - free(global_cacert); - global_cacert = NULL; - global_cacert_pem_bytes = 0; - } -} - -#if defined(CONFIG_ESP_TLS_PSK_VERIFICATION) -static esp_err_t esp_wolfssl_set_cipher_list(WOLFSSL_CTX *ctx) -{ - const char *defaultCipherList; - int ret; -#if defined(HAVE_AESGCM) && !defined(NO_DH) -#ifdef WOLFSSL_TLS13 - defaultCipherList = "DHE-PSK-AES128-GCM-SHA256:" - "TLS13-AES128-GCM-SHA256"; -#else - defaultCipherList = "DHE-PSK-AES128-GCM-SHA256"; -#endif -#elif defined(HAVE_NULL_CIPHER) - defaultCipherList = "PSK-NULL-SHA256"; -#else - defaultCipherList = "PSK-AES128-CBC-SHA256"; -#endif - ESP_LOGD(TAG, "cipher list is %s", defaultCipherList); - if ((ret = wolfSSL_CTX_set_cipher_list(ctx,defaultCipherList)) != WOLFSSL_SUCCESS) { - wolfSSL_CTX_free(ctx); - int err = wolfSSL_get_error( (WOLFSSL *)tls->priv_ssl, ret); - ESP_LOGE(TAG, "can't set cipher list, returned %d, error code: %d", ret, err); - wolfssl_print_error_msg(err); - return ESP_FAIL; - } - return ESP_OK; -} - -/* initialize the mutex before app_main() when using PSK */ -static void __attribute__((constructor)) -espt_tls_wolfssl_init_conn_lock (void) -{ - if ((tls_conn_lock = xSemaphoreCreateMutex()) == NULL) { - ESP_EARLY_LOGE(TAG, "mutex for tls psk connection could not be created"); - } -} - -/* Some callback functions required by PSK */ -static inline unsigned int esp_wolfssl_psk_client_cb(WOLFSSL* ssl, const char* hint, - char* identity, unsigned int id_max_len, unsigned char* key, - unsigned int key_max_len) -{ - (void)key_max_len; - - /* see internal.h MAX_PSK_ID_LEN for PSK identity limit */ - memcpy(identity, psk_id_str, id_max_len); - for(int count = 0; count < psk_key_max_len; count ++) { - key[count] = psk_key_array[count]; - } - xSemaphoreGive(tls_conn_lock); - return psk_key_max_len; - /* return length of key in octets or 0 or for error */ -} -#endif /* CONFIG_ESP_TLS_PSK_VERIFICATION */ diff --git a/components/esp-tls/private_include/esp_tls_private.h b/components/esp-tls/private_include/esp_tls_private.h index a4cbc9d4bd..e42407bd20 100644 --- a/components/esp-tls/private_include/esp_tls_private.h +++ b/components/esp-tls/private_include/esp_tls_private.h @@ -27,9 +27,6 @@ #ifdef CONFIG_MBEDTLS_SSL_PROTO_TLS1_3 #include "psa/crypto.h" #endif -#elif CONFIG_ESP_TLS_USING_WOLFSSL -#include "wolfssl/wolfcrypt/settings.h" -#include "wolfssl/ssl.h" #endif struct esp_tls { @@ -65,9 +62,9 @@ struct esp_tls { unsigned char *client_session; /*!< Pointer for the serialized client session ticket context. */ size_t client_session_len; /*!< Length of the serialized client session ticket context. */ #endif /* CONFIG_MBEDTLS_SSL_PROTO_TLS1_3 && CONFIG_ESP_TLS_CLIENT_SESSION_TICKETS */ -#elif CONFIG_ESP_TLS_USING_WOLFSSL - void *priv_ctx; - void *priv_ssl; +#elif CONFIG_ESP_TLS_CUSTOM_STACK + void *priv_ctx; /*!< Private context for custom TLS stack (e.g., SSL_CTX*) */ + void *priv_ssl; /*!< Private SSL handle for custom TLS stack (e.g., SSL*) */ #endif int sockfd; /*!< Underlying socket file descriptor. */ diff --git a/components/esp-tls/private_include/esp_tls_wolfssl.h b/components/esp-tls/private_include/esp_tls_wolfssl.h deleted file mode 100644 index 121c13477f..0000000000 --- a/components/esp-tls/private_include/esp_tls_wolfssl.h +++ /dev/null @@ -1,88 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2019-2021 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#pragma once -#include "esp_tls.h" -#include "esp_tls_private.h" - -/** - * Internal Callback for creating ssl handle for wolfssl - */ -int esp_create_wolfssl_handle(const char *hostname, size_t hostlen, const void *cfg, esp_tls_t *tls, void *server_params); - -/** - * Internal Callback for wolfssl_handshake - */ -int esp_wolfssl_handshake(esp_tls_t *tls, const esp_tls_cfg_t *cfg); - -/** - * Internal Callback API for wolfssl_ssl_read - */ -ssize_t esp_wolfssl_read(esp_tls_t *tls, char *data, size_t datalen); - -/** - * Internal callback API for wolfssl_ssl_write - */ -ssize_t esp_wolfssl_write(esp_tls_t *tls, const char *data, size_t datalen); - -/** - * Internal Callback for wolfssl_cleanup , frees up all the memory used by wolfssl - */ -void esp_wolfssl_cleanup(esp_tls_t *tls); - -/** - * Internal Callback for Certificate verification for wolfssl - */ -void esp_wolfssl_verify_certificate(esp_tls_t *tls); - -/** - * Internal Callback for deleting the wolfssl connection - */ -void esp_wolfssl_conn_delete(esp_tls_t *tls); - -/** - * Internal Callback for wolfssl_get_bytes_avail - */ -ssize_t esp_wolfssl_get_bytes_avail(esp_tls_t *tls); - -/** - * Callback function for setting global CA store data for TLS/SSL using wolfssl - */ -esp_err_t esp_wolfssl_set_global_ca_store(const unsigned char *cacert_pem_buf, const unsigned int cacert_pem_bytes); - -/** - * Callback function for freeing global ca store for TLS/SSL using wolfssl - */ -void esp_wolfssl_free_global_ca_store(void); - -/** - * - * Callback function for Initializing the global ca store for TLS?SSL using wolfssl - */ -esp_err_t esp_wolfssl_init_global_ca_store(void); - -/** - * Return ssl context for wolfSSL stack - */ -void *esp_wolfssl_get_ssl_context(esp_tls_t *tls); - -/** - * wolfSSL function for Initializing socket wrappers (no-operation for wolfSSL) - */ -static inline void esp_wolfssl_net_init(esp_tls_t *tls) -{ -} - - -/** - * Function to Create ESP-TLS Server session with wolfssl Stack - */ -int esp_wolfssl_server_session_create(esp_tls_cfg_server_t *cfg, int sockfd, esp_tls_t *tls); - -/* - * Delete Server Session - */ -void esp_wolfssl_server_session_delete(esp_tls_t *tls); diff --git a/components/esp-tls/test_apps/main/test_esp_tls.c b/components/esp-tls/test_apps/main/test_esp_tls.c index f91c279a11..d3175a3063 100644 --- a/components/esp-tls/test_apps/main/test_esp_tls.c +++ b/components/esp-tls/test_apps/main/test_esp_tls.c @@ -1,11 +1,12 @@ /* - * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #include "memory_checks.h" #include "esp_tls.h" +#include "esp_tls_custom_stack.h" #include "unity.h" #include "esp_err.h" #include "esp_log.h" @@ -59,6 +60,7 @@ const char *test_key_pem = "-----BEGIN PRIVATE KEY-----\n"\ "Aogx44Fozd1t2hYcozPuZD4s\n"\ "-----END PRIVATE KEY-----\n"; +#if CONFIG_ESP_TLS_USING_MBEDTLS TEST_CASE("esp-tls init deinit", "[esp-tls]") { struct esp_tls *tls = esp_tls_init(); @@ -94,3 +96,65 @@ TEST_CASE("esp_tls_server session create delete", "[esp-tls]") esp_tls_server_session_delete(tls); } +#endif /* CONFIG_ESP_TLS_USING_MBEDTLS */ + +#if CONFIG_ESP_TLS_CUSTOM_STACK +/* Stub functions for testing custom stack registration */ +static esp_err_t stub_create_ssl_handle(void *user_ctx, const char *hostname, size_t hostlen, const void *cfg, esp_tls_t *tls, void *server_params) { return ESP_OK; } +static int stub_handshake(void *user_ctx, esp_tls_t *tls, const esp_tls_cfg_t *cfg) { return 1; } +static ssize_t stub_read(void *user_ctx, esp_tls_t *tls, char *data, size_t datalen) { return 0; } +static ssize_t stub_write(void *user_ctx, esp_tls_t *tls, const char *data, size_t datalen) { return (ssize_t)datalen; } +static void stub_conn_delete(void *user_ctx, esp_tls_t *tls) { } +static void stub_net_init(void *user_ctx, esp_tls_t *tls) { } +static void *stub_get_ssl_context(void *user_ctx, esp_tls_t *tls) { return NULL; } +static ssize_t stub_get_bytes_avail(void *user_ctx, esp_tls_t *tls) { return 0; } +static esp_err_t stub_init_global_ca_store(void *user_ctx) { return ESP_OK; } +static esp_err_t stub_set_global_ca_store(void *user_ctx, const unsigned char *buf, const unsigned int len) { return ESP_OK; } +static void *stub_get_global_ca_store(void *user_ctx) { return NULL; } +static void stub_free_global_ca_store(void *user_ctx) { } +static const int stub_ciphersuites[] = { 0 }; +static const int *stub_get_ciphersuites_list(void *user_ctx) { return stub_ciphersuites; } + +static const esp_tls_stack_ops_t s_stub_ops = { + .create_ssl_handle = stub_create_ssl_handle, + .handshake = stub_handshake, + .read = stub_read, + .write = stub_write, + .conn_delete = stub_conn_delete, + .net_init = stub_net_init, + .get_ssl_context = stub_get_ssl_context, + .get_bytes_avail = stub_get_bytes_avail, + .init_global_ca_store = stub_init_global_ca_store, + .set_global_ca_store = stub_set_global_ca_store, + .get_global_ca_store = stub_get_global_ca_store, + .free_global_ca_store = stub_free_global_ca_store, + .get_ciphersuites_list = stub_get_ciphersuites_list, +}; + +TEST_CASE("esp_tls custom stack registration", "[esp-tls][custom-stack]") +{ + /* Test: registration with NULL should fail */ + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, esp_tls_register_stack(NULL, NULL)); + + /* Test: no stack registered initially */ + TEST_ASSERT_NULL(esp_tls_get_registered_stack()); + + /* Test: unregister when none registered should fail */ + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, esp_tls_unregister_stack()); + + /* Test: registration with valid ops should succeed */ + TEST_ASSERT_EQUAL(ESP_OK, esp_tls_register_stack(&s_stub_ops, NULL)); + + /* Test: get registered stack should return the ops */ + TEST_ASSERT_EQUAL_PTR(&s_stub_ops, esp_tls_get_registered_stack()); + + /* Test: double registration should fail */ + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, esp_tls_register_stack(&s_stub_ops, NULL)); + + /* Test: unregister should succeed */ + TEST_ASSERT_EQUAL(ESP_OK, esp_tls_unregister_stack()); + + /* Test: after unregister, no stack should be registered */ + TEST_ASSERT_NULL(esp_tls_get_registered_stack()); +} +#endif /* CONFIG_ESP_TLS_CUSTOM_STACK */ diff --git a/components/esp-tls/test_apps/sdkconfig.ci.custom_stack b/components/esp-tls/test_apps/sdkconfig.ci.custom_stack new file mode 100644 index 0000000000..0f7a5c7653 --- /dev/null +++ b/components/esp-tls/test_apps/sdkconfig.ci.custom_stack @@ -0,0 +1,2 @@ +# Test configuration for custom TLS stack registration feature +CONFIG_ESP_TLS_CUSTOM_STACK=y diff --git a/components/esp_common/src/esp_err_to_name.c b/components/esp_common/src/esp_err_to_name.c index b85f3a81aa..7c084e2e5a 100644 --- a/components/esp_common/src/esp_err_to_name.c +++ b/components/esp_common/src/esp_err_to_name.c @@ -745,30 +745,6 @@ static const esp_err_msg_t esp_err_msg_table[] = { # endif # ifdef ESP_ERR_MBEDTLS_SSL_READ_FAILED ERR_TBL_IT(ESP_ERR_MBEDTLS_SSL_READ_FAILED), /* 32797 0x801d mbedtls api returned failed */ -# endif -# ifdef ESP_ERR_WOLFSSL_SSL_SET_HOSTNAME_FAILED - ERR_TBL_IT(ESP_ERR_WOLFSSL_SSL_SET_HOSTNAME_FAILED), /* 32817 0x8031 wolfSSL api returned error */ -# endif -# ifdef ESP_ERR_WOLFSSL_SSL_CONF_ALPN_PROTOCOLS_FAILED - ERR_TBL_IT(ESP_ERR_WOLFSSL_SSL_CONF_ALPN_PROTOCOLS_FAILED), /* 32818 0x8032 wolfSSL api returned error */ -# endif -# ifdef ESP_ERR_WOLFSSL_CERT_VERIFY_SETUP_FAILED - ERR_TBL_IT(ESP_ERR_WOLFSSL_CERT_VERIFY_SETUP_FAILED), /* 32819 0x8033 wolfSSL api returned error */ -# endif -# ifdef ESP_ERR_WOLFSSL_KEY_VERIFY_SETUP_FAILED - ERR_TBL_IT(ESP_ERR_WOLFSSL_KEY_VERIFY_SETUP_FAILED), /* 32820 0x8034 wolfSSL api returned error */ -# endif -# ifdef ESP_ERR_WOLFSSL_SSL_HANDSHAKE_FAILED - ERR_TBL_IT(ESP_ERR_WOLFSSL_SSL_HANDSHAKE_FAILED), /* 32821 0x8035 wolfSSL api returned failed */ -# endif -# ifdef ESP_ERR_WOLFSSL_CTX_SETUP_FAILED - ERR_TBL_IT(ESP_ERR_WOLFSSL_CTX_SETUP_FAILED), /* 32822 0x8036 wolfSSL api returned failed */ -# endif -# ifdef ESP_ERR_WOLFSSL_SSL_SETUP_FAILED - ERR_TBL_IT(ESP_ERR_WOLFSSL_SSL_SETUP_FAILED), /* 32823 0x8037 wolfSSL api returned failed */ -# endif -# ifdef ESP_ERR_WOLFSSL_SSL_WRITE_FAILED - ERR_TBL_IT(ESP_ERR_WOLFSSL_SSL_WRITE_FAILED), /* 32824 0x8038 wolfSSL api returned failed */ # endif // components/esp_https_ota/include/esp_https_ota.h # ifdef ESP_ERR_HTTPS_OTA_BASE diff --git a/docs/doxygen/Doxyfile b/docs/doxygen/Doxyfile index 1fa38b280a..d5b4ecb268 100644 --- a/docs/doxygen/Doxyfile +++ b/docs/doxygen/Doxyfile @@ -256,6 +256,7 @@ INPUT = \ $(PROJECT_PATH)/components/esp_wifi/wifi_apps/nan_app/include/esp_nan.h \ $(PROJECT_PATH)/components/esp-tls/esp_tls_errors.h \ $(PROJECT_PATH)/components/esp-tls/esp_tls.h \ + $(PROJECT_PATH)/components/esp-tls/esp_tls_custom_stack.h \ $(PROJECT_PATH)/components/fatfs/diskio/diskio_impl.h \ $(PROJECT_PATH)/components/fatfs/diskio/diskio_rawflash.h \ $(PROJECT_PATH)/components/fatfs/diskio/diskio_sdmmc.h \ diff --git a/docs/en/api-reference/protocols/esp_crt_bundle.rst b/docs/en/api-reference/protocols/esp_crt_bundle.rst index 928284645e..cfb1212a65 100644 --- a/docs/en/api-reference/protocols/esp_crt_bundle.rst +++ b/docs/en/api-reference/protocols/esp_crt_bundle.rst @@ -8,10 +8,6 @@ Overview The ESP x509 Certificate Bundle API provides an easy way to include a bundle of custom x509 root certificates for TLS server verification. -.. note:: - - The bundle is currently not available when using WolfSSL. - The bundle comes with the complete list of root certificates from Mozilla's NSS root certificate store. Using the gen_crt_bundle.py python utility, the certificates' subject name and public key are stored in a file and embedded in the {IDF_TARGET_NAME} binary. When generating the bundle you may choose between: diff --git a/docs/en/api-reference/protocols/esp_tls.rst b/docs/en/api-reference/protocols/esp_tls.rst index f69e1fc58b..a91a857229 100644 --- a/docs/en/api-reference/protocols/esp_tls.rst +++ b/docs/en/api-reference/protocols/esp_tls.rst @@ -30,12 +30,12 @@ Tree Structure for ESP-TLS Component ├── esp_tls.c ├── esp_tls.h ├── esp_tls_mbedtls.c - ├── esp_tls_wolfssl.c + ├── esp_tls_custom_stack.c └── private_include ├── esp_tls_mbedtls.h - └── esp_tls_wolfssl.h + └── esp_tls_custom_stack.h -The ESP-TLS component has a file :component_file:`esp-tls/esp_tls.h` which contains the public API headers for the component. Internally, the ESP-TLS component operates using either MbedTLS or WolfSSL, which are SSL/TLS libraries. APIs specific to MbedTLS are present in :component_file:`esp-tls/private_include/esp_tls_mbedtls.h` and APIs specific to WolfSSL are present in :component_file:`esp-tls/private_include/esp_tls_wolfssl.h`. +The ESP-TLS component has a file :component_file:`esp-tls/esp_tls.h` which contains the public API headers for the component. Internally, the ESP-TLS component operates using MbedTLS as the default SSL/TLS library, or a custom TLS stack registered via the :cpp:func:`esp_tls_register_stack` API. APIs specific to MbedTLS are present in :component_file:`esp-tls/private_include/esp_tls_mbedtls.h` and APIs for custom stack registration are present in :component_file:`esp-tls/esp_tls_custom_stack.h`. .. _esp_tls_server_verification: @@ -99,72 +99,110 @@ The certificate selection callback can be configured in the :cpp:type:`esp_tls_c cert_select_cb = cert_section_callback, }; -.. _esp_tls_wolfssl: +.. _esp_tls_custom_stack: -Underlying SSL/TLS Library Options ----------------------------------- +Custom TLS Stack Support +------------------------ -The ESP-TLS component offers the option to use MbedTLS or WolfSSL as its underlying SSL/TLS library. By default, only MbedTLS is available and used, WolfSSL SSL/TLS library is also available publicly at https://github.com/espressif/esp-wolfssl. The repository provides the WolfSSL component in binary format, and it also provides a few examples that are useful for understanding the API. Please refer to the repository ``README.md`` for information on licensing and other options. Please see the below section for instructions on how to use WolfSSL in your project. +The ESP-TLS component supports registering custom TLS stack implementations via the :cpp:func:`esp_tls_register_stack` API. This allows external components to provide their own TLS stack implementation by implementing the :cpp:type:`esp_tls_stack_ops_t` interface. Once registered, all TLS connections created after the registration will use the custom stack. .. note:: - As the library options are internal to ESP-TLS, switching the libraries will not change ESP-TLS specific code for a project. + As the custom stack implementation is internal to ESP-TLS, switching to a custom stack will not change ESP-TLS specific code for a project. -How to Use WolfSSL with ESP-IDF -------------------------------- +How to Use Custom TLS Stack with ESP-IDF +---------------------------------------- -There are two ways to use WolfSSL in your project: +To use a custom TLS stack in your project, follow these steps: -- Add WolfSSL as a component directly to your project. For this, go to your project directory and run: +1. Enable the custom stack option ``CONFIG_ESP_TLS_CUSTOM_STACK`` (Component config > ESP-TLS > SSL/TLS Library > Custom TLS stack) in menuconfig. - .. code-block:: none +2. Implement all required functions defined in the :cpp:type:`esp_tls_stack_ops_t` structure. The required functions are: - mkdir components - cd components - git clone --recursive https://github.com/espressif/esp-wolfssl.git + * ``create_ssl_handle`` - Initialize TLS/SSL context for a new connection + * ``handshake`` - Perform TLS handshake + * ``read`` - Read decrypted data from TLS connection + * ``write`` - Write and encrypt data to TLS connection + * ``conn_delete`` - Clean up TLS connection and free resources + * ``net_init`` - Initialize network context + * ``get_ssl_context`` - Get stack-specific SSL context + * ``get_bytes_avail`` - Get bytes available for reading + * ``init_global_ca_store`` - Initialize global CA store + * ``set_global_ca_store`` - Load CA certificates into global store + * ``get_global_ca_store`` - Get global CA store + * ``free_global_ca_store`` - Free global CA store + * ``get_ciphersuites_list`` - Get list of supported ciphersuites -- Add WolfSSL as an extra component in your project. + Optional functions (can be NULL if not supported): - 1. Download WolfSSL with: + * ``get_client_session`` - Get client session ticket (if CONFIG_ESP_TLS_CLIENT_SESSION_TICKETS enabled) + * ``free_client_session`` - Free client session (if CONFIG_ESP_TLS_CLIENT_SESSION_TICKETS enabled) + * ``server_session_ticket_ctx_init`` - Initialize server session ticket context (if CONFIG_ESP_TLS_SERVER_SESSION_TICKETS enabled) + * ``server_session_ticket_ctx_free`` - Free server session ticket context (if CONFIG_ESP_TLS_SERVER_SESSION_TICKETS enabled) + * ``server_session_create`` - Create server session (server-side, can be NULL if server_session_init is provided) + * ``server_session_init`` - Initialize server session (server-side, can be NULL if server_session_create is provided) + * ``server_session_continue_async`` - Continue async server handshake (server-side, can be NULL if server_session_create is provided) + * ``server_session_delete`` - Delete server session (server-side, can be NULL, conn_delete will be used) - .. code-block:: none +3. Create a static/global structure containing your function implementations: - git clone --recursive https://github.com/espressif/esp-wolfssl.git + .. code-block:: c - 2. Include ESP-WolfSSL in ESP-IDF with setting ``EXTRA_COMPONENT_DIRS`` in ``CMakeLists.txt`` of your project as done in `wolfssl/examples `_. For reference see :ref:`optional_project_variable` in :doc:`build-system `. + #include "esp_tls_custom_stack.h" -After the above steps, you will have the option to choose WolfSSL as the underlying SSL/TLS library in the configuration menu of your project as follow: + static const esp_tls_stack_ops_t my_tls_ops = { + .version = ESP_TLS_STACK_OPS_VERSION, + .create_ssl_handle = my_create_ssl_handle, + .handshake = my_handshake, + .read = my_read, + .write = my_write, + .conn_delete = my_conn_delete, + .net_init = my_net_init, + .get_ssl_context = my_get_ssl_context, + .get_bytes_avail = my_get_bytes_avail, + .init_global_ca_store = my_init_global_ca_store, + .set_global_ca_store = my_set_global_ca_store, + .get_global_ca_store = my_get_global_ca_store, + .free_global_ca_store = my_free_global_ca_store, + .get_ciphersuites_list = my_get_ciphersuites_list, + // Optional functions can be NULL if not supported + .get_client_session = NULL, + .free_client_session = NULL, + .server_session_ticket_ctx_init = NULL, + .server_session_ticket_ctx_free = NULL, + .server_session_create = NULL, + .server_session_init = NULL, + .server_session_continue_async = NULL, + .server_session_delete = NULL, + }; -.. code-block:: none +4. Register your custom stack before creating any TLS connections: - idf.py menuconfig > ESP-TLS > SSL/TLS Library > Mbedtls/Wolfssl + .. code-block:: c -Comparison Between MbedTLS and WolfSSL --------------------------------------- + void app_main(void) { + // The second parameter is user context passed to global callbacks + // (init_global_ca_store, set_global_ca_store, etc.) + // Use NULL if not needed, or pass a pointer for C++ implementations + esp_err_t ret = esp_tls_register_stack(&my_tls_ops, NULL); + if (ret != ESP_OK) { + ESP_LOGE("APP", "Failed to register TLS stack: %s", esp_err_to_name(ret)); + return; + } -The following table shows a typical comparison between WolfSSL and MbedTLS when the :example:`protocols/https_request` example (which includes server authentication) is running with both SSL/TLS libraries and with all respective configurations set to default. For MbedTLS, the IN_CONTENT length and OUT_CONTENT length are set to 16384 bytes and 4096 bytes respectively. + // Now all TLS connections will use your custom stack + // ... create TLS connections as usual using esp_tls_conn_new(), etc. ... + } -.. list-table:: - :header-rows: 1 - :widths: 40 30 30 - :align: center +.. important:: - * - Property - - WolfSSL - - MbedTLS - * - Total Heap Consumed - - ~ 19 KB - - ~ 37 KB - * - Task Stack Used - - ~ 2.2 KB - - ~ 3.6 KB - * - Bin size - - ~ 858 KB - - ~ 736 KB + * The custom stack must be registered **before** creating any TLS connections. Calling :cpp:func:`esp_tls_register_stack` after TLS connections have been created will not affect existing connections. + * The :cpp:type:`esp_tls_stack_ops_t` structure must point to a static/global structure (not on the stack) as it's stored by reference. + * Your implementation should store stack-specific context data in the ``priv_ctx`` and ``priv_ssl`` fields of the :cpp:type:`esp_tls_t` structure. + * All required function pointers must be non-NULL. Optional functions can be NULL if not supported. + * The registration function can only be called once. Subsequent calls will return ``ESP_ERR_INVALID_STATE``. + * For detailed function signatures and requirements, see :component_file:`esp-tls/esp_tls_custom_stack.h`. -.. note:: - - These values can vary based on configuration options and version of respective libraries. ATECC608A (Secure Element) with ESP-TLS -------------------------------------------------- @@ -380,3 +418,4 @@ API Reference .. include-build-file:: inc/esp_tls.inc .. include-build-file:: inc/esp_tls_errors.inc +.. include-build-file:: inc/esp_tls_custom_stack.inc diff --git a/docs/en/api-reference/protocols/mbedtls.rst b/docs/en/api-reference/protocols/mbedtls.rst index 1565bcce69..0920bc9e26 100644 --- a/docs/en/api-reference/protocols/mbedtls.rst +++ b/docs/en/api-reference/protocols/mbedtls.rst @@ -177,14 +177,6 @@ Refer to the examples :example:`protocols/https_server/simple` (simple HTTPS ser If you plan to use the Mbed TLS API directly, refer to the example :example:`protocols/https_mbedtls`. This example demonstrates how to establish an HTTPS connection using Mbed TLS by setting up a secure socket with a certificate bundle for verification. -Alternatives ------------- - -:doc:`/api-reference/protocols/esp_tls` acts as an abstraction layer over the underlying SSL/TLS library and thus has an option to use Mbed TLS or wolfSSL as the underlying library. By default, only Mbed TLS is available and used in ESP-IDF whereas wolfSSL is available publicly at ``_ with the upstream submodule pointer. - -Please refer to :ref:`ESP-TLS: Underlying SSL/TLS Library Options ` documentation for more information on this and comparison of Mbed TLS and wolfSSL. - - Important Config Options ------------------------ diff --git a/docs/en/migration-guides/release-6.x/6.0/protocols.rst b/docs/en/migration-guides/release-6.x/6.0/protocols.rst index 5118ede413..6644764ec5 100644 --- a/docs/en/migration-guides/release-6.x/6.0/protocols.rst +++ b/docs/en/migration-guides/release-6.x/6.0/protocols.rst @@ -58,7 +58,44 @@ For more information: ESP-TLS ------- -**Removed Deprecated API** +Removed wolfSSL Support +~~~~~~~~~~~~~~~~~~~~~~~ + +The built-in wolfSSL TLS stack support has been removed from ESP-TLS. Users who were using wolfSSL should migrate to either: + +1. **mbedTLS (Recommended)**: The default TLS stack, fully integrated and maintained within ESP-IDF. +2. **Custom TLS Stack**: Register your own TLS implementation using the new custom stack API (see Option B below). + +**Removed Kconfig Options** + +The following Kconfig options have been removed: + +- ``CONFIG_ESP_TLS_USING_WOLFSSL`` - Use ``CONFIG_ESP_TLS_USING_MBEDTLS`` or ``CONFIG_ESP_TLS_CUSTOM_STACK`` instead +- ``CONFIG_ESP_DEBUG_WOLFSSL`` - For mbedTLS debugging, use ``CONFIG_MBEDTLS_DEBUG`` +- ``CONFIG_ESP_TLS_OCSP_CHECKALL`` - OCSP functionality should be handled by the chosen TLS stack + +**Migration Steps for wolfSSL Users** + +If your project was using wolfSSL via ESP-TLS: + +1. **Option A - Switch to mbedTLS** + + - Remove ``CONFIG_ESP_TLS_USING_WOLFSSL=y`` from your sdkconfig + - The default ``CONFIG_ESP_TLS_USING_MBEDTLS`` will be used automatically + - No code changes required for standard TLS operations + +2. **Option B - Use Custom Stack API** + + If you need to continue using wolfSSL or another TLS library, you can register it as a custom stack: + + - Enable ``CONFIG_ESP_TLS_CUSTOM_STACK`` in menuconfig + - Implement the :cpp:type:`esp_tls_stack_ops_t` interface for your TLS library + - Call :cpp:func:`esp_tls_register_stack` before creating any TLS connections + + For detailed documentation on implementing a custom TLS stack, see :ref:`esp_tls_custom_stack`. + +Removed Deprecated API +~~~~~~~~~~~~~~~~~~~~~~ The deprecated :cpp:func:`esp_tls_conn_http_new` function has been removed. Use either: diff --git a/docs/zh_CN/api-reference/protocols/esp_crt_bundle.rst b/docs/zh_CN/api-reference/protocols/esp_crt_bundle.rst index 88db4d3a17..1543aee692 100644 --- a/docs/zh_CN/api-reference/protocols/esp_crt_bundle.rst +++ b/docs/zh_CN/api-reference/protocols/esp_crt_bundle.rst @@ -8,10 +8,6 @@ ESP x509 证书包 ESP x509 证书包 API 提供了一种简便的方法,帮助你安装自定义 x509 根证书用于 TLS 服务器验证。 -.. note:: - - 目前在使用 WolfSSL 时该证书包不可用。 - 该证书包中包括 Mozilla NSS 根证书存储区的完整根证书列表。使用 gen_crt_bundle.py python 程序,可将证书的主题名称和公钥存储在文件中,并嵌入 {IDF_TARGET_NAME} 二进制文件。 生成证书包时,你需选择: diff --git a/docs/zh_CN/api-reference/protocols/esp_tls.rst b/docs/zh_CN/api-reference/protocols/esp_tls.rst index 921c8db1ff..8e11dbef63 100644 --- a/docs/zh_CN/api-reference/protocols/esp_tls.rst +++ b/docs/zh_CN/api-reference/protocols/esp_tls.rst @@ -30,12 +30,12 @@ ESP-TLS 组件的树形结构 ├── esp_tls.c ├── esp_tls.h ├── esp_tls_mbedtls.c - ├── esp_tls_wolfssl.c + ├── esp_tls_custom_stack.c └── private_include ├── esp_tls_mbedtls.h - └── esp_tls_wolfssl.h + └── esp_tls_custom_stack.h -ESP-TLS 组件文件 :component_file:`esp-tls/esp_tls.h` 包含该组件的公共 API 头文件。在 ESP-TLS 组件内部,为了实现安全会话功能,会使用 MbedTLS 和 WolfSSL 两个 SSL/TLS 库中的其中一个进行安全会话的建立,与 MbedTLS 相关的 API 存放在 :component_file:`esp-tls/private_include/esp_tls_mbedtls.h`,而与 WolfSSL 相关的 API 存放在 :component_file:`esp-tls/private_include/esp_tls_wolfssl.h`。 +ESP-TLS 组件文件 :component_file:`esp-tls/esp_tls.h` 包含该组件的公共 API 头文件。在 ESP-TLS 组件内部,默认使用 MbedTLS 作为底层 SSL/TLS 库,也可以通过 :cpp:func:`esp_tls_register_stack` API 注册自定义 TLS 协议栈。与 MbedTLS 相关的 API 存放在 :component_file:`esp-tls/private_include/esp_tls_mbedtls.h`,自定义协议栈注册相关的 API 存放在 :component_file:`esp-tls/esp_tls_custom_stack.h`。 .. _esp_tls_server_verification: @@ -99,72 +99,110 @@ ESP-TLS 服务器证书选择回调 cert_select_cb = cert_section_callback, }; -.. _esp_tls_wolfssl: +.. _esp_tls_custom_stack: -底层 SSL/TLS 库选择 -------------------------- +自定义 TLS 协议栈支持 +------------------------ -ESP-TLS 组件支持以 MbedTLS 或 WolfSSL 作为其底层 SSL/TLS 库,默认仅使用 MbedTLS,WolfSSL 的 SSL/TLS 库可在 https://github.com/espressif/esp-wolfssl 上公开获取,该仓库提供二进制格式的 WolfSSL 组件,并提供了一些示例帮助用户了解相关 API。有关许可证和其他选项,请参阅仓库的 ``README.md`` 文件。下文介绍了在工程中使用 WolfSSL 的具体流程。 +ESP-TLS 组件支持通过 :cpp:func:`esp_tls_register_stack` API 注册自定义 TLS 协议栈实现。这允许外部组件通过实现 :cpp:type:`esp_tls_stack_ops_t` 接口来提供自己的 TLS 协议栈实现。注册后,所有在此之后创建的 TLS 连接都将使用自定义协议栈。 .. note:: - 库选项位于 ESP-TLS 内部,因此切换库不会更改工程的 ESP-TLS 特定代码。 + 由于自定义协议栈的实现封装在 ESP-TLS 内部,因此切换为自定义协议栈并不会影响项目中与 ESP-TLS 相关的代码。 -在 ESP-IDF 使用 WolfSSL +在 ESP-IDF 中使用自定义 TLS 协议栈 ---------------------------------------- -要在工程中使用 WolfSSL,可采取以下两种方式: +要在工程中使用自定义 TLS 协议栈,请遵循以下步骤: -- 将 WolfSSL 作为组件直接添加到工程中。用 cd 命令进入工程目录后,使用以下命令: +1. 在 menuconfig 中启用自定义协议栈选项 ``CONFIG_ESP_TLS_CUSTOM_STACK`` (Component config > ESP-TLS > SSL/TLS Library > Custom TLS stack)。 - .. code-block:: none +2. 实现 :cpp:type:`esp_tls_stack_ops_t` 结构中定义的所有必需函数。必需函数包括: - mkdir components - cd components - git clone --recursive https://github.com/espressif/esp-wolfssl.git + * ``create_ssl_handle`` - 为新连接初始化 TLS/SSL 上下文 + * ``handshake`` - 执行 TLS 握手 + * ``read`` - 从 TLS 连接读取已解密的数据 + * ``write`` - 向 TLS 连接写入数据,并在发送前完成加密 + * ``conn_delete`` - 清理 TLS 连接并释放相关资源 + * ``net_init`` - 初始化网络上下文 + * ``get_ssl_context`` - 获取协议栈特定的 SSL 上下文 + * ``get_bytes_avail`` - 获取可用于读取的字节数 + * ``init_global_ca_store`` - 初始化全局 CA 证书存储 + * ``set_global_ca_store`` - 将 CA 证书加载到全局存储 + * ``get_global_ca_store`` - 获取全局 CA 证书存储 + * ``free_global_ca_store`` - 释放全局 CA 证书存储 + * ``get_ciphersuites_list`` - 获取支持的密码套件列表 -- 将 WolfSSL 作为额外组件添加到工程中。 + 可选函数(如果不支持,可以为 NULL): - 1. 使用以下命令下载 WolfSSL: + * ``get_client_session`` - 获取客户端会话票据(须启用 :ref:`CONFIG_ESP_TLS_CLIENT_SESSION_TICKETS`) + * ``free_client_session`` - 释放客户端会话(须启用 :ref:`CONFIG_ESP_TLS_CLIENT_SESSION_TICKETS`) + * ``server_session_ticket_ctx_init`` - 初始化服务器会话票据上下文(须启用 :ref:`CONFIG_ESP_TLS_SERVER_SESSION_TICKETS`) + * ``server_session_ticket_ctx_free`` - 释放服务器会话票据上下文(须启用 :ref:`CONFIG_ESP_TLS_SERVER_SESSION_TICKETS`) + * ``server_session_create`` - 创建服务器会话(服务器端,如果提供了 server_session_init,该接口可以为 NULL) + * ``server_session_init`` - 初始化服务器会话(服务器端,如果提供了 server_session_create,该接口可以为 NULL) + * ``server_session_continue_async`` - 继续服务器端的异步握手(服务器端,如果提供了 server_session_create,该接口可以为 NULL) + * ``server_session_delete`` - 删除服务器会话(服务器端,该接口可以为 NULL,此时将使用 ``conn_delete`` 进行清理) - .. code-block:: none +3. 创建一个包含函数实现的静态/全局结构体: - git clone https://github.com/espressif/esp-wolfssl.git + .. code-block:: c - 2. 参照 `wolfssl/examples `_ 示例,在工程的 ``CMakeLists.txt`` 文件中设置 ``EXTRA_COMPONENT_DIRS``,从而在 ESP-IDF 中包含 ESP-WolfSSL,详情请参阅 :doc:`构建系统 ` 中的 :ref:`optional_project_variable` 小节。 + #include "esp_tls_custom_stack.h" -完成上述步骤后,可以在工程配置菜单中将 WolfSSL 作为底层 SSL/TLS 库,具体步骤如下: + static const esp_tls_stack_ops_t my_tls_ops = { + .version = ESP_TLS_STACK_OPS_VERSION, + .create_ssl_handle = my_create_ssl_handle, + .handshake = my_handshake, + .read = my_read, + .write = my_write, + .conn_delete = my_conn_delete, + .net_init = my_net_init, + .get_ssl_context = my_get_ssl_context, + .get_bytes_avail = my_get_bytes_avail, + .init_global_ca_store = my_init_global_ca_store, + .set_global_ca_store = my_set_global_ca_store, + .get_global_ca_store = my_get_global_ca_store, + .free_global_ca_store = my_free_global_ca_store, + .get_ciphersuites_list = my_get_ciphersuites_list, + // 可选函数如果不支持可以为 NULL + .get_client_session = NULL, + .free_client_session = NULL, + .server_session_ticket_ctx_init = NULL, + .server_session_ticket_ctx_free = NULL, + .server_session_create = NULL, + .server_session_init = NULL, + .server_session_continue_async = NULL, + .server_session_delete = NULL, + }; -.. code-block:: none +4. 在创建任何 TLS 连接之前注册自定义协议栈: - idf.py menuconfig > ESP-TLS > SSL/TLS Library > Mbedtls/Wolfssl + .. code-block:: c -MbedTLS 与 WolfSSL 对比 --------------------------------------- + void app_main(void) { + // 第二个参数是传递给全局回调函数的用户上下文 + //(如 init_global_ca_store,set_global_ca_store 等)。 + // 如果不需要可以传 NULL,对于 C++ 实现则传递相应的指针 + esp_err_t ret = esp_tls_register_stack(&my_tls_ops, NULL); + if (ret != ESP_OK) { + ESP_LOGE("APP", "Failed to register TLS stack: %s", esp_err_to_name(ret)); + return; + } -下表是在使用 WolfSSL 和 MbedTLS 两种 SSL/TLS 库,并将所有相关配置设置为默认值时,运行具有服务器身份验证的 :example:`protocols/https_request` 示例的比较结果。对于 MbedTLS,IN_CONTENT 长度和 OUT_CONTENT 长度分别设置为 16384 字节和 4096 字节。 + // 现在所有 TLS 连接都将使用你的自定义协议栈 + // ... 像往常一样使用 esp_tls_conn_new() 等创建 TLS 连接 ... + } -.. list-table:: - :header-rows: 1 - :widths: 40 30 30 - :align: center +.. important:: - * - 属性 - - WolfSSL - - MbedTLS - * - 总消耗堆空间 - - ~ 19 KB - - ~ 37 KB - * - 任务栈使用 - - ~ 2.2 KB - - ~ 3.6 KB - * - 二进制文件大小 - - ~ 858 KB - - ~ 736 KB + * 必须在创建任何 TLS 连接 **之前** 注册自定义协议栈。在创建 TLS 连接后调用 :cpp:func:`esp_tls_register_stack` 不会影响现有连接。 + * :cpp:type:`esp_tls_stack_ops_t` 结构必须指向静态或全局结构体(不在栈上),因为系统会以引用方式存储该结构体。 + * 你的实现应将协议栈特定的上下文数据存储在 :cpp:type:`esp_tls_t` 结构的 ``priv_ctx`` 和 ``priv_ssl`` 字段中。 + * 所有必需函数的指针必须为非 NULL。可选函数如果不支持可以为 NULL。 + * 注册函数只能调用一次。后续调用将返回 ``ESP_ERR_INVALID_STATE``。 + * 更多函数签名和要求,请参阅 :component_file:`esp-tls/esp_tls_custom_stack.h`。 -.. note:: - - 若配置选项不同或相应库的版本不同,得到的值可能与上表不同。 ESP-TLS 中的 ATECC608A(安全元件) ----------------------------------------- @@ -380,3 +418,4 @@ API 参考 .. include-build-file:: inc/esp_tls.inc .. include-build-file:: inc/esp_tls_errors.inc +.. include-build-file:: inc/esp_tls_custom_stack.inc diff --git a/docs/zh_CN/api-reference/protocols/mbedtls.rst b/docs/zh_CN/api-reference/protocols/mbedtls.rst index 0e7122185d..1fbcfe5943 100644 --- a/docs/zh_CN/api-reference/protocols/mbedtls.rst +++ b/docs/zh_CN/api-reference/protocols/mbedtls.rst @@ -177,14 +177,6 @@ ESP-IDF 中的示例使用 :doc:`/api-reference/protocols/esp_tls`,为访问 如需直接使用 Mbed TLS API,请参考示例 :example:`protocols/https_mbedtls`。该示例演示了如何用 Mbed TLS 创建 HTTPS 连接。具体做法是配置安全的套接字,并使用证书包进行验证。 -替代方案 --------- - -:doc:`/api-reference/protocols/esp_tls` 是底层 SSL/TLS 库的抽象层,因此可以选择使用 Mbed TLS 或 wolfSSL 作为底层库。默认情况下,仅 Mbed TLS 可在 ESP-IDF 中使用,而 wolfSSL 在 ``_ 公开,还提供了上游子模块指针的相关信息。 - -如需了解更多相关信息或比较 Mbed TLS 和 wolfSSL,请参考文档 :ref:`ESP-TLS:底层 SSL/TLS 库选择 `。 - - 重要配置 -------- diff --git a/docs/zh_CN/migration-guides/release-6.x/6.0/protocols.rst b/docs/zh_CN/migration-guides/release-6.x/6.0/protocols.rst index df9d88c4f1..b362580da7 100644 --- a/docs/zh_CN/migration-guides/release-6.x/6.0/protocols.rst +++ b/docs/zh_CN/migration-guides/release-6.x/6.0/protocols.rst @@ -58,7 +58,44 @@ ESP-IDF 已移除内置的 ``json`` 组件。用户应迁移至使用 `IDF 组 ESP-TLS ------- -**已移除的废弃 API** +移除 wolfSSL 支持 +~~~~~~~~~~~~~~~~~ + +ESP-TLS 已移除内置的 wolfSSL TLS 协议栈支持。使用 wolfSSL 的用户应迁移至以下两个方案之一: + +1. **mbedTLS(推荐)**:默认 TLS 协议栈,完全集成并在 ESP-IDF 中维护。 +2. **自定义 TLS 协议栈**:使用自定义协议栈 API(见下文方案 B)注册自己的 TLS 实现。 + +**已移除的 Kconfig 选项** + +以下 Kconfig 选项已被移除: + +- ``CONFIG_ESP_TLS_USING_WOLFSSL`` - 请改用 ``CONFIG_ESP_TLS_USING_MBEDTLS`` 或 ``CONFIG_ESP_TLS_CUSTOM_STACK`` +- ``CONFIG_ESP_DEBUG_WOLFSSL`` - 进行 mbedTLS 调试,请使用 ``CONFIG_MBEDTLS_DEBUG`` +- ``CONFIG_ESP_TLS_OCSP_CHECKALL`` - OCSP 功能应由所选 TLS 协议栈处理 + +**wolfSSL 用户迁移步骤** + +如果你的项目通过 ESP-TLS 使用 wolfSSL: + +1. **方案 A - 切换到 mbedTLS** + + - 从 sdkconfig 中移除 ``CONFIG_ESP_TLS_USING_WOLFSSL=y`` + - 将自动使用默认的 ``CONFIG_ESP_TLS_USING_MBEDTLS`` + - 使用标准 TLS 操作,无需更改代码 + +2. **方案 B - 使用自定义协议栈 API** + + 如需要继续使用 wolfSSL 或其他 TLS 库,可以将其注册为自定义协议栈: + + - 在 menuconfig 中启用 ``CONFIG_ESP_TLS_CUSTOM_STACK`` + - 为你的 TLS 库实现 :cpp:type:`esp_tls_stack_ops_t` 接口 + - 在创建任何 TLS 连接之前调用 :cpp:func:`esp_tls_register_stack` + + 有关实现自定义 TLS 协议栈的详细文档,请参阅 :ref:`esp_tls_custom_stack`。 + +已移除的废弃 API +~~~~~~~~~~~~~~~~ 已移除废弃函数 :cpp:func:`esp_tls_conn_http_new`。请使用以下替代函数: