From a831e843fcc63246e6fd340c4a83416f3e5b9355 Mon Sep 17 00:00:00 2001 From: Colton Willey Date: Mon, 20 Apr 2026 17:27:03 -0700 Subject: [PATCH 1/2] Add Zephyr 4.3 wolfSSL TLS delivery; reorganize by Zephyr version Add zephyr/4.3/ with patch and README for wolfSSL as the default TLS backend on Zephyr 4.3. Move the existing 3.7 patch into zephyr/3.7/ alongside a version-specific README, and rewrite the top-level zephyr/README.md as a pointer to the per-version instructions. The 4.3 patch depends on wolfSSL changes submitted upstream to the wolfSSL Zephyr module (new Kconfig options for session export, peer cert retention, and always-invoke verify callback). See zephyr/4.3/README.md for build instructions. --- zephyr/3.7/README.md | 29 + zephyr/{ => 3.7}/zephyr-tls-3.7.0-rc3.patch | 0 zephyr/4.3/README.md | 33 + zephyr/4.3/zephyr-tls-4.3.0.patch | 5337 +++++++++++++++++++ zephyr/README.md | 40 +- 5 files changed, 5409 insertions(+), 30 deletions(-) create mode 100644 zephyr/3.7/README.md rename zephyr/{ => 3.7}/zephyr-tls-3.7.0-rc3.patch (100%) create mode 100644 zephyr/4.3/README.md create mode 100644 zephyr/4.3/zephyr-tls-4.3.0.patch diff --git a/zephyr/3.7/README.md b/zephyr/3.7/README.md new file mode 100644 index 00000000..3a2b17e1 --- /dev/null +++ b/zephyr/3.7/README.md @@ -0,0 +1,29 @@ +## How to setup wolfSSL support for standard Zephyr TLS Sockets and RNG (Zephyr 3.7) + +wolfSSL can also be used as the underlying implementation for the default Zephyr TLS socket interface. +With this enabled, all existing applications using the Zephyr TLS sockets will now use wolfSSL inside +for all TLS operations. This will also enable wolfSSL as the default RNG implementation. To enable this +feature, first ensure wolfSSL has been added to the west manifest using the instructions from the +README.md here: https://github.com/wolfSSL/wolfssl/tree/master/zephyr + +Once the west manifest has been updated, run west update, then run the following command to patch the sources + +``` +patch -p1 < /path/to/your/osp/zephyr/3.7/zephyr-tls-3.7.0-rc3.patch +``` + +### Run Zephyr TLS samples + +``` +west build -b samples/net/sockets/echo_server -DOVERLAY_CONFIG=overlay-wolfssl.conf +``` + +### Run Zephyr TLS tests + +``` +west build -b tests/net/socket/tls_ext/ -DOVERLAY_CONFIG=overlay-wolfssl.conf +``` + +``` +west build -b tests/net/socket/tls/ -DOVERLAY_CONFIG=overlay-wolfssl.conf +``` diff --git a/zephyr/zephyr-tls-3.7.0-rc3.patch b/zephyr/3.7/zephyr-tls-3.7.0-rc3.patch similarity index 100% rename from zephyr/zephyr-tls-3.7.0-rc3.patch rename to zephyr/3.7/zephyr-tls-3.7.0-rc3.patch diff --git a/zephyr/4.3/README.md b/zephyr/4.3/README.md new file mode 100644 index 00000000..031ce009 --- /dev/null +++ b/zephyr/4.3/README.md @@ -0,0 +1,33 @@ +## How to setup wolfSSL support for standard Zephyr TLS Sockets and RNG (Zephyr 4.3) + +wolfSSL can also be used as the underlying implementation for the default Zephyr TLS socket interface. +With this enabled, all existing applications using the Zephyr TLS sockets will now use wolfSSL inside +for all TLS operations. This will also enable wolfSSL as the default RNG implementation. To enable this +feature, first ensure wolfSSL has been added to the west manifest using the instructions from the +README.md here: https://github.com/wolfSSL/wolfssl/tree/master/zephyr + +This integration depends on the default Zephyr TLS support changes in the wolfSSL module. The required +changes are contained in wolfSSL after the merge of the associated default-TLS-support PR; use a wolfSSL +revision that includes those changes. + +Once the west manifest has been updated, run west update, then run the following command to patch the sources + +``` +patch -p1 < /path/to/your/osp/zephyr/4.3/zephyr-tls-4.3.0.patch +``` + +### Run Zephyr TLS samples + +``` +west build -b samples/net/sockets/echo_server -DEXTRA_CONF_FILE=overlay-wolfssl.conf +``` + +### Run Zephyr TLS tests + +``` +west build -b tests/net/socket/tls_ext/ -DEXTRA_CONF_FILE=overlay-wolfssl.conf +``` + +``` +west build -b tests/net/socket/tls/ -DEXTRA_CONF_FILE=overlay-wolfssl.conf +``` diff --git a/zephyr/4.3/zephyr-tls-4.3.0.patch b/zephyr/4.3/zephyr-tls-4.3.0.patch new file mode 100644 index 00000000..6c2163a2 --- /dev/null +++ b/zephyr/4.3/zephyr-tls-4.3.0.patch @@ -0,0 +1,5337 @@ +diff --git a/include/zephyr/net/socket.h b/include/zephyr/net/socket.h +index 7bad851f238..e04a648f00b 100644 +--- a/include/zephyr/net/socket.h ++++ b/include/zephyr/net/socket.h +@@ -173,7 +173,9 @@ extern "C" { + + /** Socket option for preventing certificates from being copied to the mbedTLS + * heap if possible. The option is only effective for DER certificates and is +- * ignored for PEM certificates. ++ * ignored for PEM certificates. This option has no effect when using wolfSSL ++ * as the underlying TLS implementation, the cert data is always copied to the ++ * heap in that case. + */ + #define TLS_CERT_NOCOPY 10 + /** TLS socket option to use with offloading. The option instructs the network +@@ -258,9 +260,18 @@ extern "C" { + * certificates and decide whether to proceed or abort the handshake. + * + * The option is only available if CONFIG_NET_SOCKETS_TLS_CERT_VERIFY_CALLBACK +- * Kconfig option is enabled. ++ * Kconfig option is enabled AND the mbedTLS backend is in use. Under ++ * CONFIG_WOLFSSL, setsockopt(TLS_CERT_VERIFY_CALLBACK) returns -ENOTSUP; ++ * use TLS_CERT_VERIFY_CALLBACK_WOLFSSL instead. + */ + #define TLS_CERT_VERIFY_CALLBACK 20 ++/** Write-only socket option to register a wolfSSL-style cert-verify callback. ++ * The option accepts a pointer to a @ref tls_cert_verify_cb_wolfssl structure. ++ * ++ * Only available when CONFIG_WOLFSSL_VERIFY_CALLBACK is enabled and ++ * CONFIG_WOLFSSL is the active TLS backend. ++ */ ++#define TLS_CERT_VERIFY_CALLBACK_WOLFSSL 21 + + /* Valid values for @ref TLS_PEER_VERIFY option */ + #define TLS_PEER_VERIFY_NONE 0 /**< Peer verification disabled. */ +@@ -302,6 +313,25 @@ struct tls_cert_verify_cb { + /** A pointer to an opaque context passed to the callback. */ + void *ctx; + }; ++ ++/** Data structure for @ref TLS_CERT_VERIFY_CALLBACK_WOLFSSL socket option. ++ * Only available with CONFIG_WOLFSSL. ++ */ ++struct tls_cert_verify_cb_wolfssl { ++ /** Callback with wolfSSL VerifyCallback signature: ++ * int callback(int preverify_ok, WOLFSSL_X509_STORE_CTX *ctx) ++ * ++ * Stored as void* to avoid exposing wolfSSL types in the Zephyr ++ * public header. Cast to VerifyCallback internally. ++ */ ++ void *cb; ++ ++ /** Application context pointer. Passed to the callback via ++ * WOLFSSL_X509_STORE_CTX->userCtx. If NULL, wolfSSL default ++ * behavior applies. ++ */ ++ void *ctx; ++}; + /** @} */ /* for @name */ + /** @} */ /* for @defgroup */ + +diff --git a/include/zephyr/net/tls_ciphersuites.h b/include/zephyr/net/tls_ciphersuites.h +new file mode 100644 +index 00000000000..95c60375039 +--- /dev/null ++++ b/include/zephyr/net/tls_ciphersuites.h +@@ -0,0 +1,265 @@ ++/* ++ * Copyright (c) 2018 Nordic Semiconductor ASA ++ * ++ * SPDX-License-Identifier: Apache-2.0 ++ */ ++ ++/** @file ++ * @brief TLS ciphersuite list ++ * ++ * The ciphersuite list for TLS. ++ */ ++ ++#ifndef ZEPHYR_INCLUDE_NET_TLS_CIPHERSUITES_H_ ++#define ZEPHYR_INCLUDE_NET_TLS_CIPHERSUITES_H_ ++ ++/** ++ * @brief TLS ciphersuite list ++ * @ingroup networking ++ * @{ ++ */ ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++ ++/* ++ * Supported ciphersuites (Official IANA names) ++ */ ++#define TLS_RSA_WITH_NULL_MD5 0x01 /**< Weak! */ ++#define TLS_RSA_WITH_NULL_SHA 0x02 /**< Weak! */ ++ ++#define TLS_PSK_WITH_NULL_SHA 0x2C /**< Weak! */ ++#define TLS_DHE_PSK_WITH_NULL_SHA 0x2D /**< Weak! */ ++#define TLS_RSA_PSK_WITH_NULL_SHA 0x2E /**< Weak! */ ++#define TLS_RSA_WITH_AES_128_CBC_SHA 0x2F ++ ++#define TLS_DHE_RSA_WITH_AES_128_CBC_SHA 0x33 ++#define TLS_RSA_WITH_AES_256_CBC_SHA 0x35 ++#define TLS_DHE_RSA_WITH_AES_256_CBC_SHA 0x39 ++ ++#define TLS_RSA_WITH_NULL_SHA256 0x3B /**< Weak! */ ++#define TLS_RSA_WITH_AES_128_CBC_SHA256 0x3C /**< TLS 1.2 */ ++#define TLS_RSA_WITH_AES_256_CBC_SHA256 0x3D /**< TLS 1.2 */ ++ ++#define TLS_RSA_WITH_CAMELLIA_128_CBC_SHA 0x41 ++#define TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA 0x45 ++ ++#define TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 0x67 /**< TLS 1.2 */ ++#define TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 0x6B /**< TLS 1.2 */ ++ ++#define TLS_RSA_WITH_CAMELLIA_256_CBC_SHA 0x84 ++#define TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA 0x88 ++ ++#define TLS_PSK_WITH_AES_128_CBC_SHA 0x8C ++#define TLS_PSK_WITH_AES_256_CBC_SHA 0x8D ++ ++#define TLS_DHE_PSK_WITH_AES_128_CBC_SHA 0x90 ++#define TLS_DHE_PSK_WITH_AES_256_CBC_SHA 0x91 ++ ++#define TLS_RSA_PSK_WITH_AES_128_CBC_SHA 0x94 ++#define TLS_RSA_PSK_WITH_AES_256_CBC_SHA 0x95 ++ ++#define TLS_RSA_WITH_AES_128_GCM_SHA256 0x9C /**< TLS 1.2 */ ++#define TLS_RSA_WITH_AES_256_GCM_SHA384 0x9D /**< TLS 1.2 */ ++#define TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 0x9E /**< TLS 1.2 */ ++#define TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 0x9F /**< TLS 1.2 */ ++ ++#define TLS_PSK_WITH_AES_128_GCM_SHA256 0xA8 /**< TLS 1.2 */ ++#define TLS_PSK_WITH_AES_256_GCM_SHA384 0xA9 /**< TLS 1.2 */ ++#define TLS_DHE_PSK_WITH_AES_128_GCM_SHA256 0xAA /**< TLS 1.2 */ ++#define TLS_DHE_PSK_WITH_AES_256_GCM_SHA384 0xAB /**< TLS 1.2 */ ++#define TLS_RSA_PSK_WITH_AES_128_GCM_SHA256 0xAC /**< TLS 1.2 */ ++#define TLS_RSA_PSK_WITH_AES_256_GCM_SHA384 0xAD /**< TLS 1.2 */ ++ ++#define TLS_PSK_WITH_AES_128_CBC_SHA256 0xAE ++#define TLS_PSK_WITH_AES_256_CBC_SHA384 0xAF ++#define TLS_PSK_WITH_NULL_SHA256 0xB0 /**< Weak! */ ++#define TLS_PSK_WITH_NULL_SHA384 0xB1 /**< Weak! */ ++ ++#define TLS_DHE_PSK_WITH_AES_128_CBC_SHA256 0xB2 ++#define TLS_DHE_PSK_WITH_AES_256_CBC_SHA384 0xB3 ++#define TLS_DHE_PSK_WITH_NULL_SHA256 0xB4 /**< Weak! */ ++#define TLS_DHE_PSK_WITH_NULL_SHA384 0xB5 /**< Weak! */ ++ ++#define TLS_RSA_PSK_WITH_AES_128_CBC_SHA256 0xB6 ++#define TLS_RSA_PSK_WITH_AES_256_CBC_SHA384 0xB7 ++#define TLS_RSA_PSK_WITH_NULL_SHA256 0xB8 /**< Weak! */ ++#define TLS_RSA_PSK_WITH_NULL_SHA384 0xB9 /**< Weak! */ ++ ++#define TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256 0xBA /**< TLS 1.2 */ ++#define TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 0xBE /**< TLS 1.2 */ ++ ++#define TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256 0xC0 /**< TLS 1.2 */ ++#define TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256 0xC4 /**< TLS 1.2 */ ++ ++#define TLS_ECDH_ECDSA_WITH_NULL_SHA 0xC001 /**< Weak! */ ++#define TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA 0xC004 ++#define TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA 0xC005 ++ ++#define TLS_ECDHE_ECDSA_WITH_NULL_SHA 0xC006 /**< Weak! */ ++#define TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA 0xC009 ++#define TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA 0xC00A ++ ++#define TLS_ECDH_RSA_WITH_NULL_SHA 0xC00B /**< Weak! */ ++#define TLS_ECDH_RSA_WITH_AES_128_CBC_SHA 0xC00E ++#define TLS_ECDH_RSA_WITH_AES_256_CBC_SHA 0xC00F ++ ++#define TLS_ECDHE_RSA_WITH_NULL_SHA 0xC010 /**< Weak! */ ++#define TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA 0xC013 ++#define TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA 0xC014 ++ ++#define TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 0xC023 /**< TLS 1.2 */ ++#define TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 0xC024 /**< TLS 1.2 */ ++#define TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 0xC025 /**< TLS 1.2 */ ++#define TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 0xC026 /**< TLS 1.2 */ ++#define TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 0xC027 /**< TLS 1.2 */ ++#define TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 0xC028 /**< TLS 1.2 */ ++#define TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 0xC029 /**< TLS 1.2 */ ++#define TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 0xC02A /**< TLS 1.2 */ ++ ++#define TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 0xC02B /**< TLS 1.2 */ ++#define TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 0xC02C /**< TLS 1.2 */ ++#define TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 0xC02D /**< TLS 1.2 */ ++#define TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384 0xC02E /**< TLS 1.2 */ ++#define TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 0xC02F /**< TLS 1.2 */ ++#define TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 0xC030 /**< TLS 1.2 */ ++#define TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 0xC031 /**< TLS 1.2 */ ++#define TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 0xC032 /**< TLS 1.2 */ ++ ++#define TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA 0xC035 ++#define TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA 0xC036 ++#define TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 0xC037 ++#define TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384 0xC038 ++#define TLS_ECDHE_PSK_WITH_NULL_SHA 0xC039 ++#define TLS_ECDHE_PSK_WITH_NULL_SHA256 0xC03A ++#define TLS_ECDHE_PSK_WITH_NULL_SHA384 0xC03B ++ ++#define TLS_RSA_WITH_ARIA_128_CBC_SHA256 0xC03C /**< TLS 1.2 */ ++#define TLS_RSA_WITH_ARIA_256_CBC_SHA384 0xC03D /**< TLS 1.2 */ ++#define TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256 0xC044 /**< TLS 1.2 */ ++#define TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384 0xC045 /**< TLS 1.2 */ ++#define TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256 0xC048 /**< TLS 1.2 */ ++#define TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384 0xC049 /**< TLS 1.2 */ ++#define TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256 0xC04A /**< TLS 1.2 */ ++#define TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384 0xC04B /**< TLS 1.2 */ ++#define TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256 0xC04C /**< TLS 1.2 */ ++#define TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384 0xC04D /**< TLS 1.2 */ ++#define TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256 0xC04E /**< TLS 1.2 */ ++#define TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384 0xC04F /**< TLS 1.2 */ ++#define TLS_RSA_WITH_ARIA_128_GCM_SHA256 0xC050 /**< TLS 1.2 */ ++#define TLS_RSA_WITH_ARIA_256_GCM_SHA384 0xC051 /**< TLS 1.2 */ ++#define TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256 0xC052 /**< TLS 1.2 */ ++#define TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384 0xC053 /**< TLS 1.2 */ ++#define TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256 0xC05C /**< TLS 1.2 */ ++#define TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384 0xC05D /**< TLS 1.2 */ ++#define TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256 0xC05E /**< TLS 1.2 */ ++#define TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384 0xC05F /**< TLS 1.2 */ ++#define TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256 0xC060 /**< TLS 1.2 */ ++#define TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384 0xC061 /**< TLS 1.2 */ ++#define TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256 0xC062 /**< TLS 1.2 */ ++#define TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384 0xC063 /**< TLS 1.2 */ ++#define TLS_PSK_WITH_ARIA_128_CBC_SHA256 0xC064 /**< TLS 1.2 */ ++#define TLS_PSK_WITH_ARIA_256_CBC_SHA384 0xC065 /**< TLS 1.2 */ ++#define TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256 0xC066 /**< TLS 1.2 */ ++#define TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384 0xC067 /**< TLS 1.2 */ ++#define TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256 0xC068 /**< TLS 1.2 */ ++#define TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384 0xC069 /**< TLS 1.2 */ ++#define TLS_PSK_WITH_ARIA_128_GCM_SHA256 0xC06A /**< TLS 1.2 */ ++#define TLS_PSK_WITH_ARIA_256_GCM_SHA384 0xC06B /**< TLS 1.2 */ ++#define TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256 0xC06C /**< TLS 1.2 */ ++#define TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384 0xC06D /**< TLS 1.2 */ ++#define TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256 0xC06E /**< TLS 1.2 */ ++#define TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384 0xC06F /**< TLS 1.2 */ ++#define TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256 0xC070 /**< TLS 1.2 */ ++#define TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384 0xC071 /**< TLS 1.2 */ ++ ++#define TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 0xC072 ++#define TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 0xC073 ++#define TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 0xC074 ++#define TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 0xC075 ++#define TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 0xC076 ++#define TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384 0xC077 ++#define TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256 0xC078 ++#define TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384 0xC079 ++ ++#define TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256 0xC07A /**< TLS 1.2 */ ++#define TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384 0xC07B /**< TLS 1.2 */ ++#define TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 0xC07C /**< TLS 1.2 */ ++#define TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 0xC07D /**< TLS 1.2 */ ++#define TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 0xC086 /**< TLS 1.2 */ ++#define TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 0xC087 /**< TLS 1.2 */ ++#define TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 0xC088 /**< TLS 1.2 */ ++#define TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 0xC089 /**< TLS 1.2 */ ++#define TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 0xC08A /**< TLS 1.2 */ ++#define TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 0xC08B /**< TLS 1.2 */ ++#define TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256 0xC08C /**< TLS 1.2 */ ++#define TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384 0xC08D /**< TLS 1.2 */ ++ ++#define TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256 0xC08E /**< TLS 1.2 */ ++#define TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384 0xC08F /**< TLS 1.2 */ ++#define TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256 0xC090 /**< TLS 1.2 */ ++#define TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384 0xC091 /**< TLS 1.2 */ ++#define TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256 0xC092 /**< TLS 1.2 */ ++#define TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384 0xC093 /**< TLS 1.2 */ ++ ++#define TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256 0xC094 ++#define TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384 0xC095 ++#define TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 0xC096 ++#define TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 0xC097 ++#define TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256 0xC098 ++#define TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384 0xC099 ++#define TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 0xC09A ++#define TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 0xC09B ++ ++#define TLS_RSA_WITH_AES_128_CCM 0xC09C /**< TLS 1.2 */ ++#define TLS_RSA_WITH_AES_256_CCM 0xC09D /**< TLS 1.2 */ ++#define TLS_DHE_RSA_WITH_AES_128_CCM 0xC09E /**< TLS 1.2 */ ++#define TLS_DHE_RSA_WITH_AES_256_CCM 0xC09F /**< TLS 1.2 */ ++#define TLS_RSA_WITH_AES_128_CCM_8 0xC0A0 /**< TLS 1.2 */ ++#define TLS_RSA_WITH_AES_256_CCM_8 0xC0A1 /**< TLS 1.2 */ ++#define TLS_DHE_RSA_WITH_AES_128_CCM_8 0xC0A2 /**< TLS 1.2 */ ++#define TLS_DHE_RSA_WITH_AES_256_CCM_8 0xC0A3 /**< TLS 1.2 */ ++#define TLS_PSK_WITH_AES_128_CCM 0xC0A4 /**< TLS 1.2 */ ++#define TLS_PSK_WITH_AES_256_CCM 0xC0A5 /**< TLS 1.2 */ ++#define TLS_DHE_PSK_WITH_AES_128_CCM 0xC0A6 /**< TLS 1.2 */ ++#define TLS_DHE_PSK_WITH_AES_256_CCM 0xC0A7 /**< TLS 1.2 */ ++#define TLS_PSK_WITH_AES_128_CCM_8 0xC0A8 /**< TLS 1.2 */ ++#define TLS_PSK_WITH_AES_256_CCM_8 0xC0A9 /**< TLS 1.2 */ ++#define TLS_DHE_PSK_WITH_AES_128_CCM_8 0xC0AA /**< TLS 1.2 */ ++#define TLS_DHE_PSK_WITH_AES_256_CCM_8 0xC0AB /**< TLS 1.2 */ ++/* The last two are named with PSK_DHE in the RFC, which looks like a typo */ ++ ++#define TLS_ECDHE_ECDSA_WITH_AES_128_CCM 0xC0AC /**< TLS 1.2 */ ++#define TLS_ECDHE_ECDSA_WITH_AES_256_CCM 0xC0AD /**< TLS 1.2 */ ++#define TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 0xC0AE /**< TLS 1.2 */ ++#define TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8 0xC0AF /**< TLS 1.2 */ ++ ++#define TLS_ECJPAKE_WITH_AES_128_CCM_8 0xC0FF /**< experimental */ ++ ++/* RFC 7905 */ ++#define TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 0xCCA8 /**< TLS 1.2 */ ++#define TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 0xCCA9 /**< TLS 1.2 */ ++#define TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 0xCCAA /**< TLS 1.2 */ ++#define TLS_PSK_WITH_CHACHA20_POLY1305_SHA256 0xCCAB /**< TLS 1.2 */ ++#define TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256 0xCCAC /**< TLS 1.2 */ ++#define TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256 0xCCAD /**< TLS 1.2 */ ++#define TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256 0xCCAE /**< TLS 1.2 */ ++ ++/* RFC 8446, Appendix B.4 */ ++#define TLS1_3_AES_128_GCM_SHA256 0x1301 /**< TLS 1.3 */ ++#define TLS1_3_AES_256_GCM_SHA384 0x1302 /**< TLS 1.3 */ ++#define TLS1_3_CHACHA20_POLY1305_SHA256 0x1303 /**< TLS 1.3 */ ++#define TLS1_3_AES_128_CCM_SHA256 0x1304 /**< TLS 1.3 */ ++#define TLS1_3_AES_128_CCM_8_SHA256 0x1305 /**< TLS 1.3 */ ++ ++#ifdef __cplusplus ++} ++#endif ++ ++/** ++ * @} ++ */ ++ ++#endif /* ZEPHYR_INCLUDE_NET_TLS_CIPHERSUITES_H_ */ +\ No newline at end of file +diff --git a/include/zephyr/net/tls_verify.h b/include/zephyr/net/tls_verify.h +new file mode 100644 +index 00000000000..10257d58fb5 +--- /dev/null ++++ b/include/zephyr/net/tls_verify.h +@@ -0,0 +1,62 @@ ++/* ++ * Copyright (c) 2025 wolfSSL Inc. ++ * SPDX-License-Identifier: Apache-2.0 ++ */ ++ ++/** @file ++ * @brief mbedTLS-compatible flag-bit constants for the wolfSSL backend's ++ * TLS_CERT_VERIFY_RESULT getsockopt bitmask. ++ * ++ * The wolfSSL TLS backend accumulates certificate verification errors ++ * into a tls_context field that backs the TLS_CERT_VERIFY_RESULT ++ * socket option. The bit layout matches mbedtls/x509.h so that ++ * application code inspecting the result stays portable across the ++ * two backends. ++ * ++ * Under CONFIG_MBEDTLS this file is inert; applications pull real ++ * mbedTLS types and macros from mbedtls/x509.h. ++ */ ++ ++#ifndef ZEPHYR_INCLUDE_NET_TLS_VERIFY_H_ ++#define ZEPHYR_INCLUDE_NET_TLS_VERIFY_H_ ++ ++#include ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#if defined(CONFIG_WOLFSSL) ++ ++/* Same hex values as mbedtls/x509.h */ ++#define MBEDTLS_X509_BADCERT_EXPIRED 0x01 ++#define MBEDTLS_X509_BADCERT_REVOKED 0x02 ++#define MBEDTLS_X509_BADCERT_CN_MISMATCH 0x04 ++#define MBEDTLS_X509_BADCERT_NOT_TRUSTED 0x08 ++#define MBEDTLS_X509_BADCRL_NOT_TRUSTED 0x10 ++#define MBEDTLS_X509_BADCRL_EXPIRED 0x20 ++#define MBEDTLS_X509_BADCERT_MISSING 0x40 ++#define MBEDTLS_X509_BADCERT_SKIP_VERIFY 0x80 ++#define MBEDTLS_X509_BADCERT_OTHER 0x0100 ++#define MBEDTLS_X509_BADCERT_FUTURE 0x0200 ++#define MBEDTLS_X509_BADCRL_FUTURE 0x0400 ++#define MBEDTLS_X509_BADCERT_KEY_USAGE 0x0800 ++#define MBEDTLS_X509_BADCERT_EXT_KEY_USAGE 0x1000 ++#define MBEDTLS_X509_BADCERT_NS_CERT_TYPE 0x2000 ++#define MBEDTLS_X509_BADCERT_BAD_MD 0x4000 ++#define MBEDTLS_X509_BADCERT_BAD_PK 0x8000 ++#define MBEDTLS_X509_BADCERT_BAD_KEY 0x010000 ++#define MBEDTLS_X509_BADCRL_BAD_MD 0x020000 ++#define MBEDTLS_X509_BADCRL_BAD_PK 0x040000 ++#define MBEDTLS_X509_BADCRL_BAD_KEY 0x080000 ++ ++/* Error code for verify failure (used in callback return values) */ ++#define MBEDTLS_ERR_X509_CERT_VERIFY_FAILED -0x2700 ++ ++#endif /* CONFIG_WOLFSSL */ ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* ZEPHYR_INCLUDE_NET_TLS_VERIFY_H_ */ +diff --git a/samples/net/sockets/echo_server/overlay-wolfssl.conf b/samples/net/sockets/echo_server/overlay-wolfssl.conf +new file mode 100644 +index 00000000000..9892580367a +--- /dev/null ++++ b/samples/net/sockets/echo_server/overlay-wolfssl.conf +@@ -0,0 +1,26 @@ ++# Copyright (c) 2025 wolfSSL Inc. ++# SPDX-License-Identifier: Apache-2.0 ++ ++CONFIG_MAIN_STACK_SIZE=3072 ++CONFIG_NET_BUF_RX_COUNT=80 ++CONFIG_NET_BUF_TX_COUNT=80 ++ ++# TLS configuration ++CONFIG_MBEDTLS=n ++CONFIG_NET_SAMPLE_CERTS_WITH_SC=y ++ ++CONFIG_NET_SOCKETS_SOCKOPT_TLS=y ++CONFIG_NET_SOCKETS_TLS_MAX_CONTEXTS=6 ++CONFIG_NET_SOCKETS_ENABLE_DTLS=y ++CONFIG_NET_SOCKETS_DTLS_TIMEOUT=30000 ++CONFIG_NET_SOCKETS_DTLS_MAX_FRAGMENT_LENGTH=2048 ++CONFIG_ZVFS_OPEN_MAX=20 ++ ++CONFIG_POSIX_API=y ++CONFIG_POSIX_TIMERS=y ++CONFIG_POSIX_THREADS=y ++CONFIG_WOLFSSL=y ++CONFIG_WOLFSSL_DTLS=y ++CONFIG_COMMON_LIBC_MALLOC_ARENA_SIZE=65536 ++ ++ +diff --git a/subsys/net/lib/sockets/Kconfig b/subsys/net/lib/sockets/Kconfig +index c676c32ddf1..eb3ce3a6a6b 100644 +--- a/subsys/net/lib/sockets/Kconfig ++++ b/subsys/net/lib/sockets/Kconfig +@@ -128,7 +128,7 @@ config NET_SOCKETS_SERVICE_STACK_SIZE + config NET_SOCKETS_SOCKOPT_TLS + bool "TCP TLS socket option support" + imply TLS_CREDENTIALS +- select MBEDTLS if NET_NATIVE ++ select MBEDTLS if NET_NATIVE && !WOLFSSL + imply MBEDTLS_SSL_PROTO_TLS1_2 if !NET_L2_OPENTHREAD + imply MBEDTLS_MD_C if !NET_L2_OPENTHREAD + imply MBEDTLS_RSA_C if !NET_L2_OPENTHREAD +@@ -153,15 +153,18 @@ config NET_SOCKETS_TLS_SET_MAX_FRAGMENT_LENGTH + bool "Set Maximum Fragment Length (MFL)" + default y + help +- Call mbedtls_ssl_conf_max_frag_len() on created TLS context +- configuration, so that Maximum Fragment Length (MFL) will be sent to +- peer using RFC 6066 max_fragment_length extension. ++ Configure the created TLS context so that Maximum Fragment Length (MFL) ++ will be sent to peer using RFC 6066 max_fragment_length extension. + +- Maximum Fragment Length (MFL) value is automatically chosen based on +- MBEDTLS_SSL_OUT_CONTENT_LEN and MBEDTLS_SSL_IN_CONTENT_LEN mbed TLS +- macros (which are configured by CONFIG_MBEDTLS_SSL_MAX_CONTENT_LEN in +- case of default mbed TLS config). With DTLS, MFL value may be further +- limited with NET_SOCKETS_DTLS_MAX_FRAGMENT_LENGTH. ++ For MbedTLS, Maximum Fragment Length (MFL) value is automatically ++ chosen based on MBEDTLS_SSL_OUT_CONTENT_LEN and ++ MBEDTLS_SSL_IN_CONTENT_LEN mbed TLS macros (which are configured by ++ CONFIG_MBEDTLS_SSL_MAX_CONTENT_LEN in case of default mbed TLS config). ++ With DTLS, MFL value may be further limited with ++ NET_SOCKETS_DTLS_MAX_FRAGMENT_LENGTH. ++ ++ For WolfSSL, Maximum Fragment Length (MFL) value is set by the ++ configuration option WOLFSSL_MAX_FRAGMENT_LEN. + + This is mostly useful for TLS client side to tell TLS server what is + the maximum supported receive record length. +@@ -169,7 +172,7 @@ config NET_SOCKETS_TLS_SET_MAX_FRAGMENT_LENGTH + config NET_SOCKETS_ENABLE_DTLS + bool "DTLS socket support" + depends on NET_SOCKETS_SOCKOPT_TLS +- select MBEDTLS_SSL_PROTO_DTLS if NET_NATIVE ++ select MBEDTLS_SSL_PROTO_DTLS if NET_NATIVE && !WOLFSSL + help + Enable DTLS socket support. By default only TLS over TCP is supported. + +@@ -248,7 +251,7 @@ config NET_SOCKETS_TLS_MAX_CIPHERSUITES + config NET_SOCKETS_TLS_MAX_APP_PROTOCOLS + int "Maximum number of supported application layer protocols" + default 2 +- depends on NET_SOCKETS_SOCKOPT_TLS && MBEDTLS_SSL_ALPN ++ depends on NET_SOCKETS_SOCKOPT_TLS && (MBEDTLS_SSL_ALPN || WOLFSSL_ALPN) + help + This variable sets maximum number of supported application layer + protocols over TLS/DTLS that can be set explicitly by a socket option. +@@ -263,12 +266,41 @@ config NET_SOCKETS_TLS_MAX_CLIENT_SESSION_COUNT + used for TLS/DTLS session resumption. + + config NET_SOCKETS_TLS_CERT_VERIFY_CALLBACK +- bool "TLS certificate verification callback support" ++ bool "TLS certificate verification callback support (mbedTLS backend only)" ++ depends on NET_SOCKETS_SOCKOPT_TLS ++ help ++ This option controls whether the TLS_CERT_VERIFY_CALLBACK TLS ++ socket option is available to use. When set, applications can ++ register a certificate verification callback that is invoked by ++ the TLS backend during the handshake. ++ ++ This option is only honored by the mbedTLS backend. On the ++ wolfSSL backend, setsockopt(TLS_CERT_VERIFY_CALLBACK) returns ++ -ENOTSUP. wolfSSL users should use TLS_CERT_VERIFY_CALLBACK_WOLFSSL ++ (CONFIG_WOLFSSL_VERIFY_CALLBACK) instead. ++ ++config WOLFSSL_VERIFY_CALLBACK ++ bool "wolfSSL-style cert-verify callback" ++ depends on WOLFSSL + depends on NET_SOCKETS_SOCKOPT_TLS ++ depends on !NET_SOCKETS_TLS_CERT_VERIFY_CALLBACK + help +- This option controls whether TLS_CERT_VERIFY_CALLBACK TLS socket option +- is available to use. It allows to register a certificate verification +- callback, which is called by the TLS backend during the TLS handshake. ++ Enable registration of wolfSSL-style VerifyCallback functions ++ via the TLS_CERT_VERIFY_CALLBACK_WOLFSSL socket option. When ++ enabled, the mbedTLS-style TLS_CERT_VERIFY_CALLBACK is not ++ available. ++ ++ The wolfSSL-style callback receives the standard wolfSSL ++ arguments: ++ int callback(int preverify_ok, WOLFSSL_X509_STORE_CTX *ctx) ++ ++ This option is mutually exclusive with ++ CONFIG_NET_SOCKETS_TLS_CERT_VERIFY_CALLBACK. ++ ++ When a wolfSSL-style callback is registered, TLS_CERT_VERIFY_RESULT ++ still returns accumulated MBEDTLS_X509_BADCERT_* flags for chain ++ errors observed during verification; the callback additionally ++ controls whether the handshake proceeds. + + config NET_SOCKETS_OFFLOAD + bool "Offload Socket APIs" +diff --git a/subsys/net/lib/sockets/sockets_tls.c b/subsys/net/lib/sockets/sockets_tls.c +index 2ccca60a34d..d11c1bcb9f4 100644 +--- a/subsys/net/lib/sockets/sockets_tls.c ++++ b/subsys/net/lib/sockets/sockets_tls.c +@@ -47,6 +47,12 @@ LOG_MODULE_REGISTER(net_sock_tls, CONFIG_NET_SOCKETS_LOG_LEVEL); + #include + #include + #include ++ ++#define ZTLS_IS_CLIENT MBEDTLS_SSL_IS_CLIENT ++#define ZTLS_IS_SERVER MBEDTLS_SSL_IS_SERVER ++#define ZTLS_ERROR_WANT_READ MBEDTLS_ERR_SSL_WANT_READ ++#define ZTLS_ERROR_WANT_WRITE MBEDTLS_ERR_SSL_WANT_WRITE ++ + #endif /* CONFIG_MBEDTLS */ + + #include "sockets_internal.h" +@@ -56,6 +62,36 @@ LOG_MODULE_REGISTER(net_sock_tls, CONFIG_NET_SOCKETS_LOG_LEVEL); + #include + #endif + ++#if defined(CONFIG_WOLFSSL) ++#ifndef WOLFSSL_USER_SETTINGS ++#include ++#endif ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#if defined(CONFIG_NET_SOCKETS_ENABLE_DTLS) && !defined(WOLFSSL_DTLS) ++#error "DTLS sockets enabled but wolfssl DTLS not enabled" ++#endif ++ ++#define ZTLS_IS_CLIENT 0 ++#define ZTLS_IS_SERVER 1 ++#define ZTLS_ERROR_WANT_READ WOLFSSL_ERROR_WANT_READ ++#define ZTLS_ERROR_WANT_WRITE WOLFSSL_ERROR_WANT_WRITE ++ ++/* DTLS default timeout values, copied from mbedtls to replicate existing default behavior */ ++/* ++ * Default range for DTLS retransmission timer value, in milliseconds. ++ * RFC 6347 4.2.4.1 says from 1 second to 60 seconds. ++ */ ++#define DTLS_TIMEOUT_DFL_MIN 1000 ++#define DTLS_TIMEOUT_DFL_MAX 60000 ++#endif /* CONFIG_WOLFSSL */ ++ + #if defined(CONFIG_NET_SOCKETS_TLS_MAX_APP_PROTOCOLS) + #define ALPN_MAX_PROTOCOLS (CONFIG_NET_SOCKETS_TLS_MAX_APP_PROTOCOLS + 1) + #else +@@ -115,6 +151,14 @@ struct tls_session_cache { + }; + + #if defined(CONFIG_NET_SOCKETS_ENABLE_DTLS) ++#if defined(CONFIG_WOLFSSL) ++/* Currently no support for DTLS CID, use dummy struct instead */ ++struct tls_dtls_cid { ++ bool enabled; ++ unsigned char cid[1]; ++ size_t cid_len; ++}; ++#else + struct tls_dtls_cid { + bool enabled; + unsigned char cid[MAX(MBEDTLS_SSL_CID_OUT_LEN_MAX, +@@ -122,6 +166,7 @@ struct tls_dtls_cid { + size_t cid_len; + }; + #endif ++#endif + + /** TLS context information. */ + __net_socket struct tls_context { +@@ -208,17 +253,23 @@ __net_socket struct tls_context { + bool dtls_handshake_on_connect; + #endif /* CONFIG_NET_SOCKETS_ENABLE_DTLS */ + +-#if defined(CONFIG_NET_SOCKETS_TLS_CERT_VERIFY_CALLBACK) ++#if defined(CONFIG_NET_SOCKETS_TLS_CERT_VERIFY_CALLBACK) && \ ++ !defined(CONFIG_WOLFSSL) + struct tls_cert_verify_cb cert_verify; +-#endif /* CONFIG_NET_SOCKETS_TLS_CERT_VERIFY_CALLBACK */ ++#endif /* CONFIG_NET_SOCKETS_TLS_CERT_VERIFY_CALLBACK && !CONFIG_WOLFSSL */ ++#if defined(CONFIG_WOLFSSL_VERIFY_CALLBACK) ++ struct tls_cert_verify_cb_wolfssl cert_verify_wolfssl; ++#endif /* CONFIG_WOLFSSL_VERIFY_CALLBACK */ + } options; + + #if defined(CONFIG_NET_SOCKETS_ENABLE_DTLS) + /** Context information for DTLS timing. */ + struct dtls_timing_context dtls_timing; + ++#if defined(CONFIG_MBEDTLS) + /** mbedTLS cookie context for DTLS */ + mbedtls_ssl_cookie_ctx cookie; ++#endif + + /** DTLS peer address. */ + struct sockaddr dtls_peer_addr; +@@ -227,7 +278,36 @@ __net_socket struct tls_context { + socklen_t dtls_peer_addrlen; + #endif /* CONFIG_NET_SOCKETS_ENABLE_DTLS */ + +-#if defined(CONFIG_MBEDTLS) ++#if defined(CONFIG_WOLFSSL) ++ /** The wolfSSL context */ ++ WOLFSSL_CTX *ctx; ++ ++ /** The wolfSSL SSL context */ ++ WOLFSSL *wssl; ++ ++ /** The hostname to use as the SNI */ ++ byte *host_name; ++ ++ /* Length in bytes of the host_name */ ++ word32 host_len; ++ /** Accumulated mbedTLS-compatible verify result flags. */ ++ uint32_t verify_result_flags; ++ ++#ifndef NO_PSK ++ /* The Pre Shared Key to be used */ ++ byte *psk; ++ ++ /* Length in bytes of the Pre Shared Key data */ ++ word32 psk_len; ++ ++ /* The Identity associated with the value in psk */ ++ byte *psk_id; ++ ++ /* The Length in bytes of the psk identity */ ++ word32 psk_id_len; ++#endif ++ ++#elif defined(CONFIG_MBEDTLS) + /** mbedTLS context. */ + mbedtls_ssl_context ssl; + +@@ -254,6 +334,9 @@ static struct tls_context tls_contexts[CONFIG_NET_SOCKETS_TLS_MAX_CONTEXTS]; + + static struct tls_session_cache client_cache[CONFIG_NET_SOCKETS_TLS_MAX_CLIENT_SESSION_COUNT]; + ++/* Guards client_cache. Never held across socket I/O. */ ++static K_MUTEX_DEFINE(client_cache_lock); ++ + #if defined(MBEDTLS_SSL_CACHE_C) + static mbedtls_ssl_cache_context server_cache; + #endif +@@ -266,20 +349,29 @@ static struct k_mutex context_lock; + */ + #define TLS_WAIT_MS 100 + ++static int tls_release(struct tls_context *tls); ++ ++bool net_socket_is_tls(void *obj) ++{ ++ return PART_OF_ARRAY(tls_contexts, (struct tls_context *)obj); ++} ++ + static void tls_session_cache_reset(void) + { ++ k_mutex_lock(&client_cache_lock, K_FOREVER); + for (int i = 0; i < ARRAY_SIZE(client_cache); i++) { + if (client_cache[i].session != NULL) { ++#if defined(CONFIG_WOLFSSL) ++ XFREE(client_cache[i].session, NULL, ++ DYNAMIC_TYPE_TMP_BUFFER); ++#else + mbedtls_free(client_cache[i].session); ++#endif + } + } + + (void)memset(client_cache, 0, sizeof(client_cache)); +-} +- +-bool net_socket_is_tls(void *obj) +-{ +- return PART_OF_ARRAY(tls_contexts, (struct tls_context *)obj); ++ k_mutex_unlock(&client_cache_lock); + } + + static int tls_ctr_drbg_random(void *ctx, unsigned char *buf, size_t len) +@@ -437,8 +529,10 @@ static inline void tls_set_max_frag_len(mbedtls_ssl_config *config, enum net_soc + mbedtls_ssl_conf_max_frag_len(config, mfl_code); + } + #else ++#if defined(CONFIG_MBEDTLS) + static inline void tls_set_max_frag_len(mbedtls_ssl_config *config, enum net_sock_type type) {} + #endif ++#endif + + /* Allocate TLS context. */ + static struct tls_context *tls_alloc(void) +@@ -468,6 +562,17 @@ static struct tls_context *tls_alloc(void) + if (tls) { + k_sem_init(&tls->tls_established, 0, 1); + ++#if defined(CONFIG_WOLFSSL) ++#if defined(CONFIG_NET_SOCKETS_ENABLE_DTLS) ++ tls->options.dtls_handshake_timeout_min = ++ DTLS_TIMEOUT_DFL_MIN; ++ tls->options.dtls_handshake_timeout_max = ++ DTLS_TIMEOUT_DFL_MAX; ++ tls->options.dtls_cid.cid_len = 0; ++ tls->options.dtls_cid.enabled = false; ++ tls->options.dtls_handshake_on_connect = true; ++#endif ++#else + mbedtls_ssl_init(&tls->ssl); + mbedtls_ssl_config_init(&tls->config); + #if defined(CONFIG_NET_SOCKETS_ENABLE_DTLS) +@@ -489,6 +594,7 @@ static struct tls_context *tls_alloc(void) + #if defined(CONFIG_MBEDTLS_DEBUG) + mbedtls_ssl_conf_dbg(&tls->config, zephyr_mbedtls_debug, NULL); + #endif ++#endif /* CONFIG_WOLFSSL */ + } else { + NET_WARN("Failed to allocate TLS context"); + } +@@ -512,7 +618,20 @@ static struct tls_context *tls_clone(struct tls_context *source_tls) + memcpy(&target_tls->options, &source_tls->options, + sizeof(target_tls->options)); + +-#if defined(MBEDTLS_X509_CRT_PARSE_C) ++#if defined(CONFIG_WOLFSSL) ++ if (target_tls->options.is_hostname_set && source_tls->host_name) { ++ target_tls->host_name = XMALLOC(source_tls->host_len + 1, ++ NULL, DYNAMIC_TYPE_TMP_BUFFER); ++ if (target_tls->host_name == NULL) { ++ tls_release(target_tls); ++ return NULL; ++ } ++ ++ XMEMCPY(target_tls->host_name, source_tls->host_name, ++ source_tls->host_len + 1); ++ target_tls->host_len = source_tls->host_len; ++ } ++#elif defined(MBEDTLS_X509_CRT_PARSE_C) + if (target_tls->options.is_hostname_set) { + mbedtls_ssl_set_hostname(&target_tls->ssl, + source_tls->ssl.hostname); +@@ -535,6 +654,34 @@ static int tls_release(struct tls_context *tls) + return -EBADF; + } + ++#if defined(CONFIG_WOLFSSL) ++ if (NULL != tls->host_name) { ++ XFREE(tls->host_name, NULL, DYNAMIC_TYPE_TMP_BUFFER); ++ } ++#ifndef NO_PSK ++ if (NULL != tls->psk) { ++ wc_ForceZero(tls->psk, tls->psk_len); ++ XFREE(tls->psk, NULL, DYNAMIC_TYPE_TMP_BUFFER); ++ } ++ if (NULL != tls->psk_id) { ++ XFREE(tls->psk_id, NULL, DYNAMIC_TYPE_TMP_BUFFER); ++ } ++#endif ++ if (tls->wssl != NULL) { ++ /* wolfSSL_shutdown before handshake completion can corrupt ++ * global state. ++ */ ++ if (wolfSSL_is_init_finished(tls->wssl)) { ++ (void)wolfSSL_shutdown(tls->wssl); ++ } ++ wolfSSL_free(tls->wssl); ++ tls->wssl = NULL; ++ } ++ if (tls->ctx != NULL) { ++ wolfSSL_CTX_free(tls->ctx); ++ tls->ctx = NULL; ++ } ++#else + #if defined(CONFIG_NET_SOCKETS_ENABLE_DTLS) + mbedtls_ssl_cookie_free(&tls->cookie); + #endif +@@ -545,6 +692,7 @@ static int tls_release(struct tls_context *tls) + mbedtls_x509_crt_free(&tls->own_cert); + mbedtls_pk_free(&tls->priv_key); + #endif ++#endif /* CONFIG_WOLFSSL */ + + tls->is_used = false; + +@@ -575,6 +723,7 @@ static bool peer_addr_cmp(const struct sockaddr *addr, + return false; + } + ++#if defined(CONFIG_MBEDTLS) + static int tls_session_save(const struct sockaddr *peer_addr, + mbedtls_ssl_session *session) + { +@@ -582,6 +731,8 @@ static int tls_session_save(const struct sockaddr *peer_addr, + size_t session_len; + int ret; + ++ k_mutex_lock(&client_cache_lock, K_FOREVER); ++ + for (int i = 0; i < ARRAY_SIZE(client_cache); i++) { + if (client_cache[i].session == NULL) { + /* New entry. */ +@@ -616,6 +767,7 @@ static int tls_session_save(const struct sockaddr *peer_addr, + entry->session = mbedtls_calloc(1, session_len); + if (entry->session == NULL) { + NET_ERR("Failed to allocate session buffer."); ++ k_mutex_unlock(&client_cache_lock); + return -ENOMEM; + } + +@@ -625,6 +777,7 @@ static int tls_session_save(const struct sockaddr *peer_addr, + NET_ERR("Failed to serialize session, err: -0x%x.", -ret); + mbedtls_free(entry->session); + entry->session = NULL; ++ k_mutex_unlock(&client_cache_lock); + return -ENOMEM; + } + +@@ -632,6 +785,7 @@ static int tls_session_save(const struct sockaddr *peer_addr, + entry->timestamp = k_uptime_get(); + memcpy(&entry->peer_addr, peer_addr, sizeof(*peer_addr)); + ++ k_mutex_unlock(&client_cache_lock); + return 0; + } + +@@ -641,6 +795,8 @@ static int tls_session_get(const struct sockaddr *peer_addr, + struct tls_session_cache *entry = NULL; + int ret; + ++ k_mutex_lock(&client_cache_lock, K_FOREVER); ++ + for (int i = 0; i < ARRAY_SIZE(client_cache); i++) { + if (client_cache[i].session != NULL && + peer_addr_cmp(&client_cache[i].peer_addr, peer_addr)) { +@@ -650,6 +806,7 @@ static int tls_session_get(const struct sockaddr *peer_addr, + } + + if (entry == NULL) { ++ k_mutex_unlock(&client_cache_lock); + return -ENOENT; + } + +@@ -660,9 +817,11 @@ static int tls_session_get(const struct sockaddr *peer_addr, + mbedtls_free(entry->session); + entry->session = NULL; + NET_ERR("Failed to load TLS session %d", ret); ++ k_mutex_unlock(&client_cache_lock); + return -EIO; + } + ++ k_mutex_unlock(&client_cache_lock); + return 0; + } + +@@ -735,6 +894,194 @@ static void tls_session_purge(void) + mbedtls_ssl_cache_init(&server_cache); + #endif + } ++#endif /* CONFIG_MBEDTLS */ ++ ++#if defined(CONFIG_WOLFSSL) ++/* Caller must hold client_cache_lock. */ ++static struct tls_session_cache *tls_wolfssl_session_entry_reserve( ++ const struct sockaddr *peer_addr) ++{ ++ struct tls_session_cache *entry = NULL; ++ ++ for (int i = 0; i < ARRAY_SIZE(client_cache); i++) { ++ if (client_cache[i].session == NULL) { ++ if (entry == NULL || entry->session != NULL) { ++ entry = &client_cache[i]; ++ } ++ } else { ++ if (peer_addr_cmp(&client_cache[i].peer_addr, peer_addr)) { ++ entry = &client_cache[i]; ++ break; ++ } ++ ++ if (entry == NULL || ++ (entry->session != NULL && ++ entry->timestamp < client_cache[i].timestamp)) { ++ entry = &client_cache[i]; ++ } ++ } ++ } ++ ++ if (entry == NULL) { ++ return NULL; ++ } ++ ++ if (entry->session != NULL) { ++ XFREE(entry->session, NULL, DYNAMIC_TYPE_TMP_BUFFER); ++ entry->session = NULL; ++ entry->session_len = 0; ++ } ++ ++ return entry; ++} ++ ++static void tls_session_store(struct tls_context *context, ++ const struct sockaddr *addr, ++ socklen_t addrlen) ++{ ++ WOLFSSL_SESSION *session = NULL; ++ struct tls_session_cache *entry; ++ struct sockaddr peer_addr = { 0 }; ++ unsigned char *serialized = NULL; ++ int size; ++ ++ if (!context->options.cache_enabled || context->wssl == NULL) { ++ return; ++ } ++ ++ memcpy(&peer_addr, addr, addrlen); ++ ++ session = wolfSSL_get1_session(context->wssl); ++ if (session == NULL) { ++ NET_DBG("No session to save for %p", context); ++ return; ++ } ++ ++ /* Query required buffer size. */ ++ size = wolfSSL_i2d_SSL_SESSION(session, NULL); ++ if (size <= 0) { ++ NET_ERR("Failed to size session for %p", context); ++ goto exit; ++ } ++ ++ serialized = XMALLOC((size_t)size, NULL, DYNAMIC_TYPE_TMP_BUFFER); ++ if (serialized == NULL) { ++ NET_ERR("Failed to allocate session buffer."); ++ goto exit; ++ } ++ ++ { ++ unsigned char *p = serialized; ++ ++ size = wolfSSL_i2d_SSL_SESSION(session, &p); ++ } ++ if (size <= 0) { ++ NET_ERR("Failed to serialize session for %p", context); ++ goto exit; ++ } ++ ++ k_mutex_lock(&client_cache_lock, K_FOREVER); ++ entry = tls_wolfssl_session_entry_reserve(&peer_addr); ++ if (entry == NULL) { ++ k_mutex_unlock(&client_cache_lock); ++ NET_ERR("No cache slot for %p", context); ++ goto exit; ++ } ++ ++ entry->session = serialized; ++ entry->session_len = (size_t)size; ++ entry->timestamp = k_uptime_get(); ++ memcpy(&entry->peer_addr, &peer_addr, sizeof(peer_addr)); ++ serialized = NULL; ++ k_mutex_unlock(&client_cache_lock); ++ ++exit: ++ if (serialized != NULL) { ++ XFREE(serialized, NULL, DYNAMIC_TYPE_TMP_BUFFER); ++ } ++ wolfSSL_SESSION_free(session); ++} ++ ++static void tls_session_restore(struct tls_context *context, ++ const struct sockaddr *addr, ++ socklen_t addrlen) ++{ ++ struct tls_session_cache *entry = NULL; ++ WOLFSSL_SESSION *session = NULL; ++ struct sockaddr peer_addr = { 0 }; ++ unsigned char *serialized_copy = NULL; ++ size_t serialized_len = 0; ++ const unsigned char *p; ++ ++ if (!context->options.cache_enabled || context->wssl == NULL) { ++ return; ++ } ++ ++ memcpy(&peer_addr, addr, addrlen); ++ ++ k_mutex_lock(&client_cache_lock, K_FOREVER); ++ ++ for (int i = 0; i < ARRAY_SIZE(client_cache); i++) { ++ if (client_cache[i].session != NULL && ++ peer_addr_cmp(&client_cache[i].peer_addr, &peer_addr)) { ++ entry = &client_cache[i]; ++ break; ++ } ++ } ++ ++ if (entry == NULL) { ++ k_mutex_unlock(&client_cache_lock); ++ NET_DBG("Session not found for %p", context); ++ return; ++ } ++ ++ /* Copy under lock so deserialization runs without blocking others. */ ++ serialized_copy = XMALLOC(entry->session_len, NULL, ++ DYNAMIC_TYPE_TMP_BUFFER); ++ if (serialized_copy == NULL) { ++ k_mutex_unlock(&client_cache_lock); ++ NET_ERR("Failed to allocate session copy"); ++ return; ++ } ++ memcpy(serialized_copy, entry->session, entry->session_len); ++ serialized_len = entry->session_len; ++ k_mutex_unlock(&client_cache_lock); ++ ++ p = serialized_copy; ++ session = wolfSSL_d2i_SSL_SESSION(NULL, &p, (long)serialized_len); ++ if (session == NULL) { ++ NET_ERR("Failed to load TLS session"); ++ /* Evict any stale entry that still matches this peer. */ ++ k_mutex_lock(&client_cache_lock, K_FOREVER); ++ for (int i = 0; i < ARRAY_SIZE(client_cache); i++) { ++ if (client_cache[i].session != NULL && ++ peer_addr_cmp(&client_cache[i].peer_addr, ++ &peer_addr)) { ++ XFREE(client_cache[i].session, NULL, ++ DYNAMIC_TYPE_TMP_BUFFER); ++ client_cache[i].session = NULL; ++ client_cache[i].session_len = 0; ++ break; ++ } ++ } ++ k_mutex_unlock(&client_cache_lock); ++ XFREE(serialized_copy, NULL, DYNAMIC_TYPE_TMP_BUFFER); ++ return; ++ } ++ ++ if (wolfSSL_set_session(context->wssl, session) != WOLFSSL_SUCCESS) { ++ NET_DBG("Failed to set session for %p", context); ++ } ++ ++ wolfSSL_SESSION_free(session); ++ XFREE(serialized_copy, NULL, DYNAMIC_TYPE_TMP_BUFFER); ++} ++ ++static void tls_session_purge(void) ++{ ++ tls_session_cache_reset(); ++} ++#endif /* CONFIG_WOLFSSL */ + + static inline int time_left(uint32_t start, uint32_t timeout) + { +@@ -743,6 +1090,7 @@ static inline int time_left(uint32_t start, uint32_t timeout) + return timeout - elapsed; + } + ++/* Returns: < 0 = error, 0 = timeout, 1 = data/event ready */ + static int wait(int sock, int timeout, int event) + { + struct zsock_pollfd fds = { +@@ -756,36 +1104,39 @@ static int wait(int sock, int timeout, int event) + return ret; + } + +- if (ret == 1) { +- if (fds.revents & ZSOCK_POLLNVAL) { +- return -EBADF; +- } ++ if (ret == 0) { ++ return 0; /* Timeout */ ++ } + +- if (fds.revents & ZSOCK_POLLERR) { +- int optval; +- socklen_t optlen = sizeof(optval); ++ /* ret == 1: check for error conditions */ ++ if (fds.revents & ZSOCK_POLLNVAL) { ++ return -EBADF; ++ } + +- if (zsock_getsockopt(fds.fd, SOL_SOCKET, SO_ERROR, +- &optval, &optlen) == 0) { +- NET_ERR("TLS underlying socket poll error %d", +- -optval); +- return -optval; +- } ++ if (fds.revents & ZSOCK_POLLERR) { ++ int optval; ++ socklen_t optlen = sizeof(optval); + +- return -EIO; ++ if (zsock_getsockopt(fds.fd, SOL_SOCKET, SO_ERROR, ++ &optval, &optlen) == 0) { ++ NET_ERR("TLS underlying socket poll error %d", ++ -optval); ++ return -optval; + } ++ ++ return -EIO; + } + +- return 0; ++ return 1; /* Data/event ready */ + } + + static int wait_for_reason(int sock, int timeout, int reason) + { +- if (reason == MBEDTLS_ERR_SSL_WANT_READ) { ++ if (reason == ZTLS_ERROR_WANT_READ) { + return wait(sock, timeout, ZSOCK_POLLIN); + } + +- if (reason == MBEDTLS_ERR_SSL_WANT_WRITE) { ++ if (reason == ZTLS_ERROR_WANT_WRITE) { + return wait(sock, timeout, ZSOCK_POLLOUT); + } + +@@ -854,6 +1205,71 @@ static void dtls_peer_address_get(struct tls_context *context, + *addrlen = len; + } + ++#if defined(CONFIG_WOLFSSL) ++static int dtls_wolf_tx(WOLFSSL *ssl, char *buf, int len, void *ctx) ++{ ++ struct tls_context *tls_ctx = ctx; ++ ssize_t sent; ++ ++ sent = zsock_sendto(tls_ctx->sock, buf, len, ZSOCK_MSG_DONTWAIT, ++ &tls_ctx->dtls_peer_addr, ++ tls_ctx->dtls_peer_addrlen); ++ ++ if (sent < 0) { ++ if (errno == EAGAIN) { ++ return WOLFSSL_CBIO_ERR_WANT_WRITE; ++ } ++ ++ return WOLFSSL_CBIO_ERR_GENERAL; ++ } ++ ++ return sent; ++} ++ ++static int dtls_wolf_rx(WOLFSSL *ssl, char *buf, int len, void *ctx) ++{ ++ struct tls_context *tls_ctx = ctx; ++ socklen_t addrlen = sizeof(struct sockaddr); ++ struct sockaddr addr; ++ ssize_t received; ++ ++ received = zsock_recvfrom(tls_ctx->sock, buf, len, ++ ZSOCK_MSG_DONTWAIT, &addr, &addrlen); ++ ++ if (received < 0) { ++ if (errno == EAGAIN) { ++ return WOLFSSL_CBIO_ERR_WANT_READ; ++ } ++ ++ return WOLFSSL_CBIO_ERR_GENERAL; ++ } ++ ++ if (tls_ctx->dtls_peer_addrlen == 0) { ++ /* Only allow to store peer address for DTLS servers. */ ++ if (tls_ctx->options.role == ZTLS_IS_SERVER) { ++ dtls_peer_address_set(tls_ctx, &addr, addrlen); ++ ++ if (wolfSSL_dtls_set_peer(ssl, (void *)&addr, ++ (unsigned int)addrlen) != WOLFSSL_SUCCESS) { ++ /* Roll back stored peer so retry doesn't ++ * falsely match. ++ */ ++ tls_ctx->dtls_peer_addrlen = 0; ++ return WOLFSSL_CBIO_ERR_GENERAL; ++ } ++ } else { ++ /* For clients it's incorrect to receive when ++ * no peer has been set up. ++ */ ++ return WOLFSSL_CBIO_ERR_GENERAL; ++ } ++ } else if (!dtls_is_peer_addr_valid(tls_ctx, &addr, addrlen)) { ++ return WOLFSSL_CBIO_ERR_WANT_READ; ++ } ++ ++ return received; ++} ++#else + static int dtls_tx(void *ctx, const unsigned char *buf, size_t len) + { + struct tls_context *tls_ctx = ctx; +@@ -914,8 +1330,10 @@ static int dtls_rx(void *ctx, unsigned char *buf, size_t len) + + return received; + } ++#endif /* CONFIG_WOLFSSL */ + #endif /* CONFIG_NET_SOCKETS_ENABLE_DTLS */ + ++#if defined(CONFIG_MBEDTLS) + static int tls_tx(void *ctx, const unsigned char *buf, size_t len) + { + struct tls_context *tls_ctx = ctx; +@@ -951,20 +1369,107 @@ static int tls_rx(void *ctx, unsigned char *buf, size_t len) + + return received; + } ++#endif /* CONFIG_MBEDTLS */ + +-#if defined(MBEDTLS_X509_CRT_PARSE_C) +-static bool crt_is_pem(const unsigned char *buf, size_t buflen) +-{ +- return (buflen != 0 && buf[buflen - 1] == '\0' && +- strstr((const char *)buf, "-----BEGIN CERTIFICATE-----") != NULL); +-} +-#endif +- +-static int tls_add_ca_certificate(struct tls_context *tls, +- struct tls_credential *ca_cert) ++#if defined(CONFIG_WOLFSSL) ++static int tls_wolf_tx(WOLFSSL *ssl, char *buf, int len, void *ctx) + { +-#if defined(MBEDTLS_X509_CRT_PARSE_C) +- int err; ++ struct tls_context *tls_ctx = ctx; ++ ssize_t sent; ++ ++ sent = zsock_sendto(tls_ctx->sock, buf, len, ++ ZSOCK_MSG_DONTWAIT, NULL, 0); ++ ++ if (sent < 0) { ++ switch (errno) { ++ case EAGAIN: ++ return WOLFSSL_CBIO_ERR_WANT_WRITE; ++ case ECONNRESET: ++ return WOLFSSL_CBIO_ERR_CONN_RST; ++ case EINTR: ++ return WOLFSSL_CBIO_ERR_ISR; ++ case EPIPE: ++ return WOLFSSL_CBIO_ERR_CONN_CLOSE; ++ default: ++ return WOLFSSL_CBIO_ERR_GENERAL; ++ } ++ } ++ ++ return sent; ++} ++ ++static int tls_wolf_rx(WOLFSSL *ssl, char *buf, int len, void *ctx) ++{ ++ struct tls_context *tls_ctx = ctx; ++ ssize_t received; ++ ++ received = zsock_recvfrom(tls_ctx->sock, buf, len, ++ ZSOCK_MSG_DONTWAIT, NULL, 0); ++ ++ if (received < 0) { ++ switch (errno) { ++ case EAGAIN: ++ if (!wolfSSL_dtls(ssl)) { ++ return WOLFSSL_CBIO_ERR_WANT_READ; ++ } else { ++ return WOLFSSL_CBIO_ERR_TIMEOUT; ++ } ++ case ECONNRESET: ++ return WOLFSSL_CBIO_ERR_CONN_RST; ++ case EINTR: ++ return WOLFSSL_CBIO_ERR_ISR; ++ case ECONNREFUSED: ++ return WOLFSSL_CBIO_ERR_WANT_READ; ++ case ECONNABORTED: ++ return WOLFSSL_CBIO_ERR_CONN_CLOSE; ++ default: ++ return WOLFSSL_CBIO_ERR_GENERAL; ++ } ++ } else if (received == 0) { ++ return WOLFSSL_CBIO_ERR_CONN_CLOSE; ++ } ++ ++ return received; ++} ++ ++/* wolfssl implementation also used this check for private keys, so only check ++ * -----BEGIN to match -----BEGIN CERTIFICATE and -----BEGIN PRIVATE KEY */ ++static bool crt_is_pem(const unsigned char *buf, size_t buflen) ++{ ++ return (buflen != 0 && buf[buflen - 1] == '\0' && ++ strstr((const char *)buf, "-----BEGIN") != NULL); ++} ++#endif /* CONFIG_WOLFSSL */ ++ ++#if defined(MBEDTLS_X509_CRT_PARSE_C) ++static bool crt_is_pem(const unsigned char *buf, size_t buflen) ++{ ++ return (buflen != 0 && buf[buflen - 1] == '\0' && ++ strstr((const char *)buf, "-----BEGIN CERTIFICATE-----") != NULL); ++} ++#endif ++ ++static int tls_add_ca_certificate(struct tls_context *tls, ++ struct tls_credential *ca_cert) ++{ ++#if defined(CONFIG_WOLFSSL) ++ int ret; ++ int format = WOLFSSL_FILETYPE_ASN1; ++ ++ if (crt_is_pem(ca_cert->buf, ca_cert->len)) { ++ format = WOLFSSL_FILETYPE_PEM; ++ } ++ ret = wolfSSL_CTX_load_verify_buffer(tls->ctx, ca_cert->buf, ++ ca_cert->len, format); ++ ++ if (ret != WOLFSSL_SUCCESS) { ++ NET_ERR("Failed to parse CA certificate"); ++ return -EINVAL; ++ } ++ ++ return 0; ++#elif defined(MBEDTLS_X509_CRT_PARSE_C) ++ int err; + + if (tls->options.cert_nocopy == TLS_CERT_NOCOPY_NONE || + crt_is_pem(ca_cert->buf, ca_cert->len)) { +@@ -987,6 +1492,7 @@ static int tls_add_ca_certificate(struct tls_context *tls, + return -ENOTSUP; + } + ++#if defined(CONFIG_MBEDTLS) + static void tls_set_ca_chain(struct tls_context *tls) + { + #if defined(MBEDTLS_X509_CRT_PARSE_C) +@@ -995,11 +1501,27 @@ static void tls_set_ca_chain(struct tls_context *tls) + &mbedtls_x509_crt_profile_default); + #endif /* MBEDTLS_X509_CRT_PARSE_C */ + } ++#endif + + static int tls_add_own_cert(struct tls_context *tls, + struct tls_credential *own_cert) + { +-#if defined(MBEDTLS_X509_CRT_PARSE_C) ++#if defined(CONFIG_WOLFSSL) ++ int ret = 0; ++ int format = WOLFSSL_FILETYPE_ASN1; ++ ++ if (crt_is_pem(own_cert->buf, own_cert->len)) { ++ format = WOLFSSL_FILETYPE_PEM; ++ } ++ ret = wolfSSL_CTX_use_certificate_buffer(tls->ctx, own_cert->buf, ++ own_cert->len, format); ++ if (ret != WOLFSSL_SUCCESS) { ++ NET_ERR("Failed to parse certificate"); ++ return -EINVAL; ++ } ++ ++ return 0; ++#elif defined(MBEDTLS_X509_CRT_PARSE_C) + int err; + + if (tls->options.cert_nocopy == TLS_CERT_NOCOPY_NONE || +@@ -1022,6 +1544,7 @@ static int tls_add_own_cert(struct tls_context *tls, + return -ENOTSUP; + } + ++#if defined(CONFIG_MBEDTLS) + static int tls_set_own_cert(struct tls_context *tls) + { + #if defined(MBEDTLS_X509_CRT_PARSE_C) +@@ -1036,11 +1559,27 @@ static int tls_set_own_cert(struct tls_context *tls) + + return -ENOTSUP; + } ++#endif + + static int tls_set_private_key(struct tls_context *tls, + struct tls_credential *priv_key) + { +-#if defined(MBEDTLS_X509_CRT_PARSE_C) ++#if defined(CONFIG_WOLFSSL) ++ int ret = 0; ++ int format = WOLFSSL_FILETYPE_ASN1; ++ ++ if (crt_is_pem(priv_key->buf, priv_key->len)) { ++ format = WOLFSSL_FILETYPE_PEM; ++ } ++ ret = wolfSSL_CTX_use_PrivateKey_buffer(tls->ctx, priv_key->buf, ++ priv_key->len, format); ++ if (ret != WOLFSSL_SUCCESS) { ++ NET_ERR("Failed to parse private key"); ++ return -EINVAL; ++ } ++ ++ return 0; ++#elif defined(MBEDTLS_X509_CRT_PARSE_C) + int err; + + err = mbedtls_pk_parse_key(&tls->priv_key, priv_key->buf, +@@ -1060,6 +1599,43 @@ static int tls_set_psk(struct tls_context *tls, + struct tls_credential *psk, + struct tls_credential *psk_id) + { ++#if defined(CONFIG_WOLFSSL) ++#ifndef NO_PSK ++ if (NULL != tls->psk) { ++ wc_ForceZero(tls->psk, tls->psk_len); ++ XFREE(tls->psk, NULL, DYNAMIC_TYPE_TMP_BUFFER); ++ } ++ tls->psk = (byte *)XMALLOC( ++ psk->len, NULL, DYNAMIC_TYPE_TMP_BUFFER); ++ if (tls->psk == NULL) { ++ return -ENOMEM; ++ } ++ ++ XMEMCPY(tls->psk, psk->buf, psk->len); ++ tls->psk_len = psk->len; ++ ++ if (NULL != tls->psk_id) { ++ XFREE(tls->psk_id, NULL, DYNAMIC_TYPE_TMP_BUFFER); ++ } ++ tls->psk_id = (byte *)XMALLOC( ++ psk_id->len + 1, NULL, DYNAMIC_TYPE_TMP_BUFFER); ++ if (tls->psk_id == NULL) { ++ wc_ForceZero(tls->psk, tls->psk_len); ++ XFREE(tls->psk, NULL, DYNAMIC_TYPE_TMP_BUFFER); ++ tls->psk = NULL; ++ tls->psk_len = 0; ++ return -ENOMEM; ++ } ++ ++ XMEMCPY(tls->psk_id, psk_id->buf, psk_id->len); ++ tls->psk_id[psk_id->len] = '\0'; ++ tls->psk_id_len = psk_id->len; ++ ++ return 0; ++#else ++ return -ENOTSUP; ++#endif ++#else + #if defined(MBEDTLS_SSL_HANDSHAKE_WITH_PSK_ENABLED) + int err = mbedtls_ssl_conf_psk(&tls->config, + psk->buf, psk->len, +@@ -1071,6 +1647,7 @@ static int tls_set_psk(struct tls_context *tls, + + return 0; + #endif ++#endif /* CONFIG_WOLFSSL */ + + return -ENOTSUP; + } +@@ -1113,6 +1690,7 @@ static int tls_set_credential(struct tls_context *tls, + return 0; + } + ++#if defined(CONFIG_MBEDTLS) + static int tls_mbedtls_set_credentials(struct tls_context *tls) + { + struct tls_credential *cred; +@@ -1234,7 +1812,7 @@ static int tls_mbedtls_handshake(struct tls_context *context, + #endif /* CONFIG_NET_SOCKETS_ENABLE_DTLS */ + + ret = wait_for_reason(context->sock, timeout_ms, ret); +- if (ret != 0) { ++ if (ret < 0) { + break; + } + +@@ -1463,10 +2041,29 @@ static int tls_mbedtls_init(struct tls_context *context, bool is_server) + + return 0; + } ++#endif /* CONFIG_MBEDTLS */ + + static int tls_check_cert(struct tls_credential *cert) + { +-#if defined(MBEDTLS_X509_CRT_PARSE_C) ++#if defined(CONFIG_WOLFSSL) ++ WOLFSSL_X509 *x509; ++ int fmt; ++ ++ fmt = crt_is_pem(cert->buf, cert->len) ? ++ WOLFSSL_FILETYPE_PEM : WOLFSSL_FILETYPE_ASN1; ++ ++ x509 = wolfSSL_X509_load_certificate_buffer(cert->buf, ++ (int)cert->len, fmt); ++ if (x509 == NULL) { ++ NET_ERR("Failed to parse %s on tag %d", ++ "certificate", cert->tag); ++ return -EINVAL; ++ } ++ ++ wolfSSL_X509_free(x509); ++ ++ return 0; ++#elif defined(MBEDTLS_X509_CRT_PARSE_C) + mbedtls_x509_crt cert_ctx; + int err; + +@@ -1501,7 +2098,31 @@ static int tls_check_cert(struct tls_credential *cert) + + static int tls_check_priv_key(struct tls_credential *priv_key) + { +-#if defined(MBEDTLS_X509_CRT_PARSE_C) ++#if defined(CONFIG_WOLFSSL) ++ WOLFSSL_CTX *tmp_ctx; ++ int fmt; ++ int ret; ++ ++ tmp_ctx = wolfSSL_CTX_new(wolfSSLv23_client_method()); ++ if (tmp_ctx == NULL) { ++ return -ENOMEM; ++ } ++ ++ fmt = crt_is_pem(priv_key->buf, priv_key->len) ? ++ WOLFSSL_FILETYPE_PEM : WOLFSSL_FILETYPE_ASN1; ++ ++ ret = wolfSSL_CTX_use_PrivateKey_buffer(tmp_ctx, priv_key->buf, ++ (long)priv_key->len, fmt); ++ wolfSSL_CTX_free(tmp_ctx); ++ ++ if (ret != WOLFSSL_SUCCESS) { ++ NET_ERR("Failed to parse %s on tag %d", ++ "private key", priv_key->tag); ++ return -EINVAL; ++ } ++ ++ return 0; ++#elif defined(MBEDTLS_X509_CRT_PARSE_C) + mbedtls_pk_context key_ctx; + int err; + +@@ -1529,7 +2150,22 @@ static int tls_check_priv_key(struct tls_credential *priv_key) + + static int tls_check_psk(struct tls_credential *psk) + { +-#if defined(MBEDTLS_SSL_HANDSHAKE_WITH_PSK_ENABLED) ++#if defined(CONFIG_WOLFSSL) ++ struct tls_credential *psk_id; ++ ++ psk_id = credential_get(psk->tag, TLS_CREDENTIAL_PSK_ID); ++ if (psk_id == NULL) { ++ NET_ERR("No matching PSK ID found for tag %d", psk->tag); ++ return -EINVAL; ++ } ++ ++ if (psk->len == 0 || psk_id->len == 0) { ++ NET_ERR("PSK or PSK ID empty on tag %d", psk->tag); ++ return -EINVAL; ++ } ++ ++ return 0; ++#elif defined(MBEDTLS_SSL_HANDSHAKE_WITH_PSK_ENABLED) + struct tls_credential *psk_id; + + psk_id = credential_get(psk->tag, TLS_CREDENTIAL_PSK_ID); +@@ -1600,19 +2236,1000 @@ static int tls_check_credentials(const sec_tag_t *sec_tags, int sec_tag_count) + } + } + +- /* If no credential is found with such a tag, report an error. */ +- if (!tag_found) { +- NET_ERR("No TLS credential found with tag %d", tag); +- err = -ENOENT; +- goto exit; ++ /* If no credential is found with such a tag, report an error. */ ++ if (!tag_found) { ++ NET_ERR("No TLS credential found with tag %d", tag); ++ err = -ENOENT; ++ goto exit; ++ } ++ } ++ ++exit: ++ credentials_unlock(); ++ ++ return err; ++} ++ ++#if defined(CONFIG_WOLFSSL) ++ ++static int tls_wolfssl_reset(struct tls_context *context) ++{ ++ k_sem_reset(&context->tls_established); ++ ++ context->verify_result_flags = 0; ++ context->error = 0; ++ ++#if defined(CONFIG_NET_SOCKETS_ENABLE_DTLS) ++ /* Server role: reset the address so that a new ++ * client can connect w/o a need to reopen a socket ++ * Client role: keep peer addr so socket can continue to be used ++ * even on handshake timeout ++ */ ++ if (context->options.role == ZTLS_IS_SERVER) { ++ (void)memset(&context->dtls_peer_addr, 0, ++ sizeof(context->dtls_peer_addr)); ++ context->dtls_peer_addrlen = 0; ++ } ++#endif ++ ++ return 0; ++} ++ ++static ssize_t send_tls_wolfssl(struct tls_context *ctx, const void *buf, ++ size_t len, int flags) ++{ ++ const bool is_block = is_blocking(ctx->sock, flags); ++ int ret = 0; ++ int err = 0; ++ k_timeout_t timeout; ++ k_timepoint_t end; ++ ++ if (ctx->error != 0) { ++ errno = ctx->error; ++ return -1; ++ } ++ ++ if (ctx->session_closed) { ++ errno = ECONNABORTED; ++ return -1; ++ } ++ ++ if (!is_block) { ++ timeout = K_NO_WAIT; ++ } else { ++ timeout = ctx->options.timeout_tx; ++ } ++ ++ end = sys_timepoint_calc(timeout); ++ ++ do { ++ ret = wolfSSL_write(ctx->wssl, buf, len); ++ if (ret > 0) { ++ return ret; ++ } ++ ++ err = wolfSSL_get_error(ctx->wssl, ret); ++ ++ if (err == WOLFSSL_ERROR_WANT_READ || ++ err == WOLFSSL_ERROR_WANT_WRITE) { ++ int timeout_ms; ++ ++ if (!is_block) { ++ errno = EAGAIN; ++ break; ++ } ++ ++ /* Blocking timeout. */ ++ timeout = sys_timepoint_timeout(end); ++ if (K_TIMEOUT_EQ(timeout, K_NO_WAIT)) { ++ errno = EAGAIN; ++ break; ++ } ++ ++ /* Block. */ ++ timeout_ms = timeout_to_ms(&timeout); ++ ret = wait_for_reason(ctx->sock, timeout_ms, err); ++ if (ret < 0) { ++ errno = -ret; ++ break; ++ } ++ } else { ++ NET_ERR("TLS send error: %x", err); ++ tls_wolfssl_reset(ctx); ++ ctx->error = ECONNABORTED; ++ errno = ECONNABORTED; ++ break; ++ } ++ } while (true); ++ ++ return -1; ++} ++ ++static ssize_t recv_tls_wolfssl(struct tls_context *ctx, void *buf, ++ size_t max_len, int flags) ++{ ++ size_t recv_len = 0; ++ const bool waitall = flags & ZSOCK_MSG_WAITALL; ++ const bool is_block = is_blocking(ctx->sock, flags); ++ k_timeout_t timeout; ++ k_timepoint_t end; ++ int ret; ++ int err; ++ ++ if (ctx->error != 0) { ++ errno = ctx->error; ++ return -1; ++ } ++ ++ if (ctx->session_closed) { ++ return 0; ++ } ++ ++ if (!is_block) { ++ timeout = K_NO_WAIT; ++ } else { ++ timeout = ctx->options.timeout_rx; ++ } ++ ++ end = sys_timepoint_calc(timeout); ++ ++ do { ++ size_t read_len = max_len - recv_len; ++ ++ ret = wolfSSL_read(ctx->wssl, (uint8_t *)buf + recv_len, ++ read_len); ++ if (ret < 0) { ++ err = wolfSSL_get_error(ctx->wssl, ret); ++ if (err == WOLFSSL_ERROR_WANT_READ || ++ err == WOLFSSL_ERROR_WANT_WRITE) { ++ int timeout_ms; ++ ++ if (!is_block) { ++ ret = -EAGAIN; ++ goto err; ++ } ++ ++ /* Blocking timeout. */ ++ timeout = sys_timepoint_timeout(end); ++ if (K_TIMEOUT_EQ(timeout, K_NO_WAIT)) { ++ ret = -EAGAIN; ++ goto err; ++ } ++ ++ timeout_ms = timeout_to_ms(&timeout); ++ ++ /* Block. */ ++ k_mutex_unlock(ctx->lock); ++ ret = wait_for_reason(ctx->sock, timeout_ms, err); ++ k_mutex_lock(ctx->lock, K_FOREVER); ++ ++ if (ret >= 0) { ++ /* Retry. */ ++ continue; ++ } ++ } else if (err == WOLFSSL_ERROR_ZERO_RETURN || ++ err == SOCKET_PEER_CLOSED_E) { ++ ctx->session_closed = true; ++ break; ++ } else { ++ NET_ERR("TLS recv error: %x", err); ++ ret = -EIO; ++ } ++ ++err: ++ errno = -ret; ++ return -1; ++ } ++ ++ if (ret == 0) { ++ break; ++ } ++ ++ recv_len += ret; ++ } while ((recv_len == 0) || (waitall && (recv_len < max_len))); ++ ++ return recv_len; ++} ++ ++static int tls_wolfssl_connect(struct tls_context *context, k_timeout_t timeout) ++{ ++ int ret = 0; ++ int err = 0; ++ k_timepoint_t end; ++ ++ context->handshake_in_progress = true; ++ ++ end = sys_timepoint_calc(timeout); ++ ++ while ((ret = wolfSSL_connect(context->wssl)) != WOLFSSL_SUCCESS) { ++ err = wolfSSL_get_error(context->wssl, ret); ++ if (err == WOLFSSL_ERROR_WANT_READ || ++ err == WOLFSSL_ERROR_WANT_WRITE) { ++ int timeout_ms; ++ ++ /* Blocking timeout. */ ++ timeout = sys_timepoint_timeout(end); ++ if (K_TIMEOUT_EQ(timeout, K_NO_WAIT)) { ++ ret = -EAGAIN; ++ break; ++ } ++ ++ /* Block. */ ++ timeout_ms = timeout_to_ms(&timeout); ++ ++#if defined(CONFIG_NET_SOCKETS_ENABLE_DTLS) ++ if (context->type == SOCK_DGRAM) { ++ int timeout_dtls = ++ wolfSSL_dtls_get_current_timeout(context->wssl); ++ ++ if (timeout_ms == SYS_FOREVER_MS) { ++ timeout_ms = timeout_dtls; ++ } else { ++ timeout_ms = MIN(timeout_dtls, ++ timeout_ms); ++ } ++ } ++#endif /* CONFIG_NET_SOCKETS_ENABLE_DTLS */ ++ ++ ret = wait_for_reason(context->sock, timeout_ms, err); ++ ++ if (ret < 0) { ++ break; ++ } ++ ++#if defined(CONFIG_NET_SOCKETS_ENABLE_DTLS) ++ if (context->type == SOCK_DGRAM && ret == 0) { ++ ret = wolfSSL_dtls_got_timeout(context->wssl); ++ if (ret != WOLFSSL_SUCCESS) { ++ err = wolfSSL_get_error(context->wssl, ret); ++ if (err != WOLFSSL_ERROR_WANT_READ && ++ err != WOLFSSL_ERROR_WANT_WRITE) { ++ ret = -ETIMEDOUT; ++ break; ++ } ++ } ++ } ++#endif ++ ++ continue; ++ } else { ++ NET_ERR("TLS handshake error: %x", err); ++ tls_wolfssl_reset(context); ++ context->error = ECONNABORTED; ++ ret = -ECONNABORTED; ++ break; ++ } ++ } ++ ++ if (ret == WOLFSSL_SUCCESS) { ++ k_sem_give(&context->tls_established); ++ } ++ ++ context->handshake_in_progress = false; ++ ++ return ret; ++} ++ ++static int tls_wolfssl_accept(struct tls_context *context, k_timeout_t timeout) ++{ ++ int ret = 0; ++ int err = 0; ++ k_timepoint_t end; ++ ++ context->handshake_in_progress = true; ++ ++ end = sys_timepoint_calc(timeout); ++ ++ while ((ret = wolfSSL_accept(context->wssl)) != WOLFSSL_SUCCESS) { ++ err = wolfSSL_get_error(context->wssl, ret); ++ if (err == WOLFSSL_ERROR_WANT_READ || ++ err == WOLFSSL_ERROR_WANT_WRITE) { ++ int timeout_ms; ++ ++ /* Blocking timeout. */ ++ timeout = sys_timepoint_timeout(end); ++ if (K_TIMEOUT_EQ(timeout, K_NO_WAIT)) { ++ ret = -EAGAIN; ++ break; ++ } ++ ++ /* Block. */ ++ timeout_ms = timeout_to_ms(&timeout); ++ ++#if defined(CONFIG_NET_SOCKETS_ENABLE_DTLS) ++ if (context->type == SOCK_DGRAM) { ++ int timeout_dtls = ++ wolfSSL_dtls_get_current_timeout(context->wssl); ++ ++ if (timeout_ms == SYS_FOREVER_MS) { ++ timeout_ms = timeout_dtls; ++ } else { ++ timeout_ms = MIN(timeout_dtls, ++ timeout_ms); ++ } ++ } ++#endif /* CONFIG_NET_SOCKETS_ENABLE_DTLS */ ++ ++ ret = wait_for_reason(context->sock, timeout_ms, err); ++ ++ if (ret < 0) { ++ break; ++ } ++ ++#if defined(CONFIG_NET_SOCKETS_ENABLE_DTLS) ++ if (context->type == SOCK_DGRAM && ret == 0) { ++ ret = wolfSSL_dtls_got_timeout(context->wssl); ++ if (ret != WOLFSSL_SUCCESS) { ++ err = wolfSSL_get_error(context->wssl, ret); ++ if (err != WOLFSSL_ERROR_WANT_READ && ++ err != WOLFSSL_ERROR_WANT_WRITE) { ++ ret = -ETIMEDOUT; ++ break; ++ } ++ } ++ } ++#endif ++ ++ continue; ++ } else { ++ NET_ERR("TLS handshake error: %x", err); ++ tls_wolfssl_reset(context); ++ context->error = ECONNABORTED; ++ ret = -ECONNABORTED; ++ break; ++ } ++ } ++ ++ if (ret == WOLFSSL_SUCCESS) { ++ k_sem_give(&context->tls_established); ++ } ++ ++ context->handshake_in_progress = false; ++ ++ return ret; ++} ++ ++#ifndef NO_PSK ++static unsigned int tls_psk_server_cb(WOLFSSL *ssl, const char *identity, ++ unsigned char *key, ++ unsigned int key_max_len) ++{ ++ struct tls_context *context = NULL; ++ ++ context = (struct tls_context *)wolfSSL_get_psk_callback_ctx(ssl); ++ if (context == NULL) { ++ return 0; ++ } ++ ++ if (context->psk == NULL || context->psk_id == NULL) { ++ return 0; ++ } ++ ++ if (context->psk_len == 0 || context->psk_len > key_max_len) { ++ return 0; ++ } ++ ++ if (XSTRCMP(identity, context->psk_id) != 0) { ++ return 0; ++ } ++ ++ XMEMCPY(key, context->psk, context->psk_len); ++ ++ return context->psk_len; ++} ++ ++static unsigned int tls_psk_client_cb(WOLFSSL *ssl, const char *hint, ++ char *identity, ++ unsigned int id_max_len, ++ unsigned char *key, ++ unsigned int key_max_len) ++{ ++ struct tls_context *context = NULL; ++ ++ context = (struct tls_context *)wolfSSL_get_psk_callback_ctx(ssl); ++ if (context == NULL) { ++ return 0; ++ } ++ ++ if (context->psk == NULL || context->psk_id == NULL) { ++ return 0; ++ } ++ ++ if (context->psk_len == 0 || context->psk_len > key_max_len || ++ context->psk_id_len == 0 || ++ context->psk_id_len + 1 > id_max_len) { ++ return 0; ++ } ++ ++ XMEMCPY(identity, context->psk_id, context->psk_id_len); ++ identity[context->psk_id_len] = '\0'; ++ XMEMCPY(key, context->psk, context->psk_len); ++ ++ return context->psk_len; ++} ++#endif ++ ++static int tls_wolfssl_set_credentials(struct tls_context *tls) ++{ ++ struct tls_credential *cred; ++ sec_tag_t tag; ++ int i, err = 0; ++ bool tag_found; ++ ++ credentials_lock(); ++ ++ for (i = 0; i < tls->options.sec_tag_list.sec_tag_count; i++) { ++ tag = tls->options.sec_tag_list.sec_tags[i]; ++ cred = NULL; ++ tag_found = false; ++ ++ while ((cred = credential_next_get(tag, cred)) != NULL) { ++ tag_found = true; ++ ++ err = tls_set_credential(tls, cred); ++ if (err != 0) { ++ goto exit; ++ } ++ } ++ ++ if (!tag_found) { ++ err = -ENOENT; ++ goto exit; ++ } ++ } ++ ++exit: ++ credentials_unlock(); ++ ++ return err; ++} ++ ++static int tls_wolfssl_set_session_cache_mode(struct tls_context *context) ++{ ++ if ((context->options.role == ZTLS_IS_SERVER) && ++ (0 == context->options.cache_enabled)) { ++ if (wolfSSL_CTX_set_session_cache_mode(context->ctx, ++ SSL_SESS_CACHE_OFF) != WOLFSSL_SUCCESS) { ++ return -EINVAL; ++ } ++ } ++ ++ return 0; ++} ++ ++static int tls_wolfssl_set_hostname(struct tls_context *context) ++{ ++ if (!context->options.is_hostname_set) { ++ /* Empty domain forces CN/SAN mismatch for unset hostname — ++ * MITM protection matching mbedTLS's ++ * mbedtls_ssl_set_hostname(&ssl, ""). ++ */ ++ if (context->options.role == ZTLS_IS_CLIENT) { ++ if (wolfSSL_check_domain_name(context->wssl, "") ++ != WOLFSSL_SUCCESS) { ++ return -EINVAL; ++ } ++ } ++ return 0; ++ } ++ ++ if (context->options.role == ZTLS_IS_CLIENT) { ++ if (wolfSSL_UseSNI(context->wssl, WOLFSSL_SNI_HOST_NAME, ++ (const char *)context->host_name, ++ context->host_len) != WOLFSSL_SUCCESS) { ++ return -EINVAL; ++ } ++ ++ if (wolfSSL_check_domain_name(context->wssl, ++ (const char *)context->host_name) != WOLFSSL_SUCCESS) { ++ return -EINVAL; ++ } ++ } else { ++ /* mbedTLS doesn't do server-side SNI; skip UseSNI here. */ ++ if (wolfSSL_check_domain_name(context->wssl, ++ (const char *)context->host_name) != WOLFSSL_SUCCESS) { ++ return -EINVAL; ++ } ++ } ++ ++ return 0; ++} ++ ++/* Map wolfSSL verify error to mbedTLS flag bits. */ ++static uint32_t tls_wolfssl_error_to_mbedtls_flags(int error) ++{ ++ switch (error) { ++ case 0: ++ return 0; ++ case ASN_AFTER_DATE_E: ++ return MBEDTLS_X509_BADCERT_EXPIRED; ++ case ASN_BEFORE_DATE_E: ++ return MBEDTLS_X509_BADCERT_FUTURE; ++ case ASN_NO_SIGNER_E: ++ case ASN_SELF_SIGNED_E: ++ return MBEDTLS_X509_BADCERT_NOT_TRUSTED; ++ case CRL_CERT_REVOKED: ++ case OCSP_CERT_REVOKED: ++ return MBEDTLS_X509_BADCERT_REVOKED; ++ case DOMAIN_NAME_MISMATCH: ++ return MBEDTLS_X509_BADCERT_CN_MISMATCH; ++ case ASN_SIG_CONFIRM_E: ++ case ASN_SIG_HASH_E: ++ case ASN_SIG_KEY_E: ++ return MBEDTLS_X509_BADCERT_NOT_TRUSTED; ++ case EXT_NOT_ALLOWED: ++ case KEYUSAGE_E: ++ return MBEDTLS_X509_BADCERT_KEY_USAGE; ++ case EXTKEYUSAGE_E: ++ return MBEDTLS_X509_BADCERT_EXT_KEY_USAGE; ++ case CRL_CERT_DATE_ERR: ++ return MBEDTLS_X509_BADCRL_EXPIRED; ++ case ASN_CRL_NO_SIGNER_E: ++ return MBEDTLS_X509_BADCRL_NOT_TRUSTED; ++ case ASN_PATHLEN_SIZE_E: ++ case ASN_PATHLEN_INV_E: ++ case MAX_CHAIN_ERROR: ++ case NOT_CA_ERROR: ++ return MBEDTLS_X509_BADCERT_NOT_TRUSTED; ++ case ASN_NO_PEM_HEADER: ++ return MBEDTLS_X509_BADCERT_MISSING; ++ default: ++ return MBEDTLS_X509_BADCERT_OTHER; ++ } ++} ++ ++static int tls_wolfssl_verify_accumulate_cb(int preverify_ok, ++ WOLFSSL_X509_STORE_CTX *store) ++{ ++ struct tls_context *context; ++ ++ if (store == NULL || store->userCtx == NULL) { ++ return preverify_ok; ++ } ++ ++ context = (struct tls_context *)store->userCtx; ++ ++ if (!preverify_ok && store->error != 0) { ++ context->verify_result_flags |= ++ tls_wolfssl_error_to_mbedtls_flags(store->error); ++ } ++ ++ /* OPTIONAL: return 1 so handshake continues after recording flags. */ ++ if (context->options.verify_level == TLS_PEER_VERIFY_OPTIONAL) { ++ return 1; ++ } ++ ++ return preverify_ok; ++} ++ ++#if defined(CONFIG_WOLFSSL_VERIFY_CALLBACK) ++static int tls_wolfssl_verify_cb_wrapper(int preverify_ok, ++ WOLFSSL_X509_STORE_CTX *store) ++{ ++ struct tls_context *context; ++ VerifyCallback user_cb; ++ void *user_ctx; ++ int ret; ++ ++ if (store == NULL || store->userCtx == NULL) { ++ return preverify_ok; ++ } ++ ++ context = (struct tls_context *)store->userCtx; ++ ++ if (!preverify_ok && store->error != 0) { ++ context->verify_result_flags |= ++ tls_wolfssl_error_to_mbedtls_flags(store->error); ++ } ++ ++ user_cb = (VerifyCallback)context->options.cert_verify_wolfssl.cb; ++ user_ctx = context->options.cert_verify_wolfssl.ctx; ++ ++ /* Swap userCtx so the customer callback sees its registered ctx, ++ * restore ours afterwards for subsequent chain-position callbacks. ++ */ ++ store->userCtx = user_ctx; ++ ret = user_cb(preverify_ok, store); ++ store->userCtx = context; ++ ++ return ret; ++} ++#endif /* CONFIG_WOLFSSL_VERIFY_CALLBACK */ ++ ++static int tls_wolfssl_set_verify(struct tls_context *context) ++{ ++ int verifyLevel = -1; ++ VerifyCallback cb = NULL; ++ ++ if (context->options.verify_level != -1) { ++ switch (context->options.verify_level) { ++ case TLS_PEER_VERIFY_NONE: ++ verifyLevel = WOLFSSL_VERIFY_NONE; ++ break; ++ case TLS_PEER_VERIFY_OPTIONAL: ++ if (context->options.role == ZTLS_IS_SERVER) { ++ verifyLevel = WOLFSSL_VERIFY_PEER; ++ } else { ++ verifyLevel = WOLFSSL_VERIFY_PEER; ++ } ++ break; ++ case TLS_PEER_VERIFY_REQUIRED: ++ if (context->options.role == ZTLS_IS_SERVER) { ++ verifyLevel = WOLFSSL_VERIFY_PEER | ++ WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT | ++ WOLFSSL_VERIFY_FAIL_EXCEPT_PSK; ++ } else { ++ verifyLevel = WOLFSSL_VERIFY_PEER; ++ } ++ break; ++ default: ++ return -EINVAL; ++ } ++ } ++ ++ context->verify_result_flags = 0; ++ ++ /* SetCertCbCtx plants tls_context so the chosen callback can reach ++ * verify_result_flags. ++ */ ++#if defined(CONFIG_WOLFSSL_VERIFY_CALLBACK) ++ if (context->options.cert_verify_wolfssl.cb != NULL) { ++ wolfSSL_SetCertCbCtx(context->wssl, context); ++ cb = tls_wolfssl_verify_cb_wrapper; ++ } else { ++ wolfSSL_SetCertCbCtx(context->wssl, context); ++ cb = tls_wolfssl_verify_accumulate_cb; ++ } ++#else ++ wolfSSL_SetCertCbCtx(context->wssl, context); ++ cb = tls_wolfssl_verify_accumulate_cb; ++#endif ++ ++ if (verifyLevel != -1 || cb != NULL) { ++ if (verifyLevel == -1) { ++ /* Match mbedTLS defaults: required for clients, ++ * none for servers. ++ */ ++ if (context->options.role == ZTLS_IS_SERVER) { ++ verifyLevel = WOLFSSL_VERIFY_NONE; ++ } else { ++ verifyLevel = WOLFSSL_VERIFY_PEER; ++ } ++ } ++ wolfSSL_set_verify(context->wssl, verifyLevel, cb); ++ } ++ ++ return 0; ++} ++ ++static int tls_wolfssl_set_ciphersuites(struct tls_context *context) ++{ ++ int i = 0; ++ int cipher_cnt = 0; ++ uint32_t tmp = 0; ++ uint16_t sh = 0; ++ byte *cs = NULL; ++ byte *cs_bytes = NULL; ++ byte *iter = NULL; ++ byte arr[2]; ++ ++ /* Nothing to set */ ++ if (context->options.ciphersuites[0] == 0) { ++ return 0; ++ } ++ ++ i = 0; ++ while (context->options.ciphersuites[i] != 0) { ++ cipher_cnt++; ++ i++; ++ } ++ ++ cs = (byte *)context->options.ciphersuites; ++ cs_bytes = XMALLOC(cipher_cnt * 2, NULL, DYNAMIC_TYPE_TMP_BUFFER); ++ if (cs_bytes == NULL) { ++ return -ENOMEM; ++ } ++ ++ XMEMSET(cs_bytes, 0, cipher_cnt * 2); ++ ++ /* Convert mbedtls style integer format to wolfssl ++ * cipher byte style (2 bytes only per ciphersuite) */ ++ iter = cs; ++ i = 0; ++ while (iter < (cs + (cipher_cnt * sizeof(int)))) { ++ tmp = *((int *)iter); ++ /* All ciphersuites are 2 byte values, high order bytes should not be set */ ++ if (tmp > 0xFFFF) { ++ XFREE(cs_bytes, NULL, DYNAMIC_TYPE_TMP_BUFFER); ++ return -EINVAL; ++ } ++ /* Check above guarantees this is not a narrowing conversion */ ++ sh = (uint16_t)tmp; ++ ++ /* Convert from host order 16 bit int to big endian array */ ++ sys_put_be16(sh, arr); ++ cs_bytes[i] = arr[0]; ++ cs_bytes[i + 1] = arr[1]; ++ i += 2; ++ iter += sizeof(int); ++ } ++ ++ if (wolfSSL_set_cipher_list_bytes(context->wssl, cs_bytes, ++ cipher_cnt * 2) != WOLFSSL_SUCCESS) { ++ XFREE(cs_bytes, NULL, DYNAMIC_TYPE_TMP_BUFFER); ++ return -EINVAL; ++ } ++ ++ XFREE(cs_bytes, NULL, DYNAMIC_TYPE_TMP_BUFFER); ++ return 0; ++} ++ ++#ifdef HAVE_ALPN ++static int tls_wolfssl_set_alpn(struct tls_context *context) ++{ ++ byte *alpn_buf = NULL; ++ int alpn_buf_len = 0; ++ byte *iter = NULL; ++ int len = 0; ++ int i = 0; ++ ++ if (ALPN_MAX_PROTOCOLS && context->options.alpn_list[0] != NULL) { ++ /* Convert from mbedtls format array of char * to single char * ++ * comma delimited list */ ++ i = 0; ++ while (context->options.alpn_list[i] != NULL) { ++ alpn_buf_len += XSTRLEN(context->options.alpn_list[i]) + 1; ++ i++; ++ } ++ ++ alpn_buf = XMALLOC(alpn_buf_len, NULL, DYNAMIC_TYPE_TMP_BUFFER); ++ if (alpn_buf == NULL) { ++ return -ENOMEM; ++ } ++ ++ i = 0; ++ iter = alpn_buf; ++ while (context->options.alpn_list[i] != NULL) { ++ len = XSTRLEN(context->options.alpn_list[i]); ++ XMEMCPY(iter, context->options.alpn_list[i], len); ++ iter += len; ++ *iter++ = ','; ++ i++; ++ } ++ ++ /* Replace final trailing comma with NULL terminator */ ++ *(iter - 1) = '\0'; ++ if (wolfSSL_UseALPN(context->wssl, alpn_buf, alpn_buf_len - 1, ++ WOLFSSL_ALPN_FAILED_ON_MISMATCH) != WOLFSSL_SUCCESS) { ++ XFREE(alpn_buf, NULL, DYNAMIC_TYPE_TMP_BUFFER); ++ return -EINVAL; ++ } ++ ++ XFREE(alpn_buf, NULL, DYNAMIC_TYPE_TMP_BUFFER); ++ } ++ ++ return 0; ++} ++#endif ++ ++#if defined(CONFIG_NET_SOCKETS_ENABLE_DTLS) ++static int tls_wolfssl_set_dtls_timeouts(struct tls_context *context) ++{ ++ if (context->type == SOCK_DGRAM) { ++ if (wolfSSL_dtls_set_timeout_max(context->wssl, ++ (int)context->options.dtls_handshake_timeout_max) != WOLFSSL_SUCCESS) { ++ return -EINVAL; ++ } ++ if (wolfSSL_dtls_set_timeout_init(context->wssl, ++ (int)context->options.dtls_handshake_timeout_min) != WOLFSSL_SUCCESS) { ++ return -EINVAL; ++ } ++ ++ /* Underlying socket always acts like non-blocking due to IO callback overrides */ ++ wolfSSL_dtls_set_using_nonblock(context->wssl, 1); ++ } ++ ++ return 0; ++} ++#endif ++ ++static int tls_wolfssl_set_options(struct tls_context *context) ++{ ++ int ret = 0; ++ bool is_server = FALSE; ++ ++ if (context->options.role == ZTLS_IS_SERVER) { ++ is_server = TRUE; ++ } ++ ++ ret = tls_wolfssl_set_verify(context); ++ if (ret != 0) { ++ return ret; ++ } ++ ++ ret = tls_wolfssl_set_hostname(context); ++ if (ret != 0) { ++ return ret; ++ } ++ ++ ret = tls_wolfssl_set_session_cache_mode(context); ++ if (ret != 0) { ++ return ret; ++ } ++ ++ ret = tls_wolfssl_set_ciphersuites(context); ++ if (ret != 0) { ++ return ret; ++ } ++ ++#ifdef HAVE_ALPN ++ ret = tls_wolfssl_set_alpn(context); ++ if (ret != 0) { ++ return ret; ++ } ++#endif ++ ++#if defined(CONFIG_NET_SOCKETS_ENABLE_DTLS) ++ ret = tls_wolfssl_set_dtls_timeouts(context); ++ if (ret != 0) { ++ return ret; ++ } ++#endif ++ ++ return 0; ++} ++ ++static WOLFSSL_METHOD *tls_wolfssl_get_method(struct tls_context *context, ++ bool is_server) ++{ ++ if (context->type == SOCK_STREAM) { ++ switch (context->tls_version) { ++#ifdef WOLFSSL_TLS13 ++ case IPPROTO_TLS_1_3: ++ return is_server ? wolfTLSv1_3_server_method() ++ : wolfTLSv1_3_client_method(); ++#endif ++#ifndef WOLFSSL_NO_TLS12 ++ case IPPROTO_TLS_1_2: ++ return is_server ? wolfTLSv1_2_server_method() ++ : wolfTLSv1_2_client_method(); ++#endif ++ case IPPROTO_TLS_1_1: ++ case IPPROTO_TLS_1_0: ++ /* user_settings.h defines NO_OLD_TLS. */ ++ return NULL; ++ default: ++ return NULL; ++ } ++ } ++#if defined(CONFIG_NET_SOCKETS_ENABLE_DTLS) && defined(WOLFSSL_DTLS) ++ else if (context->type == SOCK_DGRAM) { ++#ifndef WOLFSSL_NO_TLS12 ++ return is_server ? wolfDTLSv1_2_server_method() ++ : wolfDTLSv1_2_client_method(); ++#else ++ return NULL; ++#endif ++ } ++#endif ++ return NULL; ++} ++ ++static int tls_wolfssl_init(struct tls_context *context, bool is_server) ++{ ++ WOLFSSL_METHOD *method; ++ int ret; ++ ++ context->options.role = is_server ? ZTLS_IS_SERVER : ZTLS_IS_CLIENT; ++ ++#if defined(CONFIG_WOLFSSL_DEBUG) ++ wolfSSL_Debugging_ON(); ++#endif ++ ++ method = tls_wolfssl_get_method(context, is_server); ++ ++ if (method == NULL) { ++ return -ENOTSUP; ++ } ++ ++ if (context->ctx == NULL) { ++ context->ctx = wolfSSL_CTX_new(method); ++ if (context->ctx == NULL) { ++ return -ENOMEM; ++ } ++ } ++ ++ if (context->type == SOCK_STREAM) { ++ wolfSSL_CTX_SetIORecv(context->ctx, tls_wolf_rx); ++ wolfSSL_CTX_SetIOSend(context->ctx, tls_wolf_tx); ++ } ++#if defined(CONFIG_NET_SOCKETS_ENABLE_DTLS) ++ else if (context->type == SOCK_DGRAM) { ++ wolfSSL_CTX_SetIORecv(context->ctx, dtls_wolf_rx); ++ wolfSSL_CTX_SetIOSend(context->ctx, dtls_wolf_tx); ++ } ++#endif ++ else { ++ return -EINVAL; ++ } ++ ++ ret = tls_wolfssl_set_credentials(context); ++ if (ret != 0) { ++ goto err_cleanup; ++ } ++ ++#ifndef NO_PSK ++ if (NULL != context->psk) { ++ if (is_server) { ++ wolfSSL_CTX_set_psk_server_callback(context->ctx, ++ tls_psk_server_cb); ++ } else { ++ wolfSSL_CTX_set_psk_client_callback(context->ctx, ++ tls_psk_client_cb); ++ } ++ } ++#endif ++ ++#if defined(CONFIG_WOLFSSL_MAX_FRAGMENT_LEN) ++ if (wolfSSL_CTX_UseMaxFragment(context->ctx, ++ CONFIG_WOLFSSL_MAX_FRAGMENT_LEN) != WOLFSSL_SUCCESS) { ++ ret = -EINVAL; ++ goto err_cleanup; ++ } ++#endif ++ ++ if (NULL == context->wssl) { ++ if ((context->wssl = wolfSSL_new(context->ctx)) == NULL) { ++ ret = -EINVAL; ++ goto err_cleanup; + } + } + +-exit: +- credentials_unlock(); ++#if defined(HAVE_SECURE_RENEGOTIATION) ++ if (wolfSSL_UseSecureRenegotiation(context->wssl) != WOLFSSL_SUCCESS) { ++ ret = -EINVAL; ++ goto err_cleanup; ++ } ++#endif + +- return err; ++ if ((ret = wolfSSL_set_fd(context->wssl, ++ context->sock)) != WOLFSSL_SUCCESS) { ++ ret = -EINVAL; ++ goto err_cleanup; ++ } ++ ++ /* Set our TLS context as the read/write context since it contains the socket */ ++ wolfSSL_SetIOReadCtx(context->wssl, (void *)context); ++ wolfSSL_SetIOWriteCtx(context->wssl, (void *)context); ++ ++#ifndef NO_PSK ++ if (NULL != context->psk) { ++ wolfSSL_set_psk_callback_ctx(context->wssl, (void *)context); ++ } ++#endif ++ ++ ret = tls_wolfssl_set_options(context); ++ if (ret != 0) { ++ goto err_cleanup; ++ } ++ ++ context->is_initialized = true; ++ ++ return 0; ++ ++err_cleanup: ++ if (context->wssl != NULL) { ++ wolfSSL_free(context->wssl); ++ context->wssl = NULL; ++ } ++ if (context->ctx != NULL) { ++ wolfSSL_CTX_free(context->ctx); ++ context->ctx = NULL; ++ } ++ ++ return ret; + } ++#endif /* CONFIG_WOLFSSL */ + + static int tls_opt_sec_tag_list_set(struct tls_context *context, + const void *optval, socklen_t optlen) +@@ -1680,6 +3297,36 @@ static int tls_opt_sec_tag_list_get(struct tls_context *context, + static int tls_opt_hostname_set(struct tls_context *context, + const void *optval, socklen_t optlen) + { ++#if defined(CONFIG_WOLFSSL) ++ if (NULL != context->host_name) { ++ XFREE(context->host_name, NULL, DYNAMIC_TYPE_TMP_BUFFER); ++ context->host_name = NULL; ++ context->host_len = 0; ++ } ++ ++ if (optval == NULL) { ++ context->options.is_hostname_set = false; ++ return 0; ++ } ++ ++ /* +1 for NUL — wolfSSL APIs require C strings. */ ++ context->host_name = XMALLOC(optlen + 1, NULL, DYNAMIC_TYPE_TMP_BUFFER); ++ if (context->host_name == NULL) { ++ context->options.is_hostname_set = false; ++ return -ENOMEM; ++ } ++ ++ XMEMCPY(context->host_name, optval, optlen); ++ context->host_name[optlen] = '\0'; ++ context->host_len = optlen; ++ context->options.is_hostname_set = true; ++ ++ if (context->wssl != NULL) { ++ return tls_wolfssl_set_hostname(context); ++ } ++ ++ return 0; ++#else + ARG_UNUSED(optlen); + + #if defined(MBEDTLS_X509_CRT_PARSE_C) +@@ -1693,6 +3340,7 @@ static int tls_opt_hostname_set(struct tls_context *context, + context->options.is_hostname_set = true; + + return 0; ++#endif /* CONFIG_WOLFSSL */ + } + + static int tls_opt_ciphersuite_list_set(struct tls_context *context, +@@ -1715,12 +3363,23 @@ static int tls_opt_ciphersuite_list_set(struct tls_context *context, + return -EINVAL; + } + ++#if defined(CONFIG_WOLFSSL) ++ XMEMCPY(context->options.ciphersuites, optval, optlen); ++ context->options.ciphersuites[cipher_cnt] = 0; ++ ++ if (context->wssl != NULL) { ++ return tls_wolfssl_set_ciphersuites(context); ++ } ++ ++ return 0; ++#else + memcpy(context->options.ciphersuites, optval, optlen); + context->options.ciphersuites[cipher_cnt] = 0; + + mbedtls_ssl_conf_ciphersuites(&context->config, + context->options.ciphersuites); + return 0; ++#endif /* CONFIG_WOLFSSL */ + } + + static int tls_opt_ciphersuite_list_get(struct tls_context *context, +@@ -1734,6 +3393,49 @@ static int tls_opt_ciphersuite_list_get(struct tls_context *context, + return -EINVAL; + } + ++#if defined(CONFIG_WOLFSSL) ++ if (context->options.ciphersuites[0] != 0) { ++ /* Return user-configured ciphersuites */ ++ selected_ciphers = context->options.ciphersuites; ++ cipher_cnt = *optlen / sizeof(int); ++ while (selected_ciphers[i] != 0) { ++ ciphers[i] = selected_ciphers[i]; ++ if (++i == cipher_cnt) { ++ break; ++ } ++ } ++ *optlen = i * sizeof(int); ++ return 0; ++ } else { ++ const char *cipherName; ++ int numCipherSuites = 0; ++ byte b1, b2; ++ int flags = 0; ++ ++ cipher_cnt = *optlen / sizeof(int); ++ ++ while ((cipherName = wolfSSL_get_cipher_list(numCipherSuites)) != NULL) { ++ if (wolfSSL_get_cipher_suite_from_name(cipherName, ++ &b1, &b2, ++ &flags) != 0) { ++ numCipherSuites++; ++ continue; ++ } ++ ++ byte cs_arr[2] = { b1, b2 }; ++ ++ ciphers[i] = (int)sys_get_be16(cs_arr); ++ ++ if (++i == cipher_cnt) { ++ break; ++ } ++ numCipherSuites++; ++ } ++ ++ *optlen = i * sizeof(int); ++ return 0; ++ } ++#else + if (context->options.ciphersuites[0] == 0) { + /* No specific ciphersuites configured, return all available. */ + selected_ciphers = mbedtls_ssl_list_ciphersuites(); +@@ -1753,23 +3455,51 @@ static int tls_opt_ciphersuite_list_get(struct tls_context *context, + *optlen = i * sizeof(int); + + return 0; ++#endif /* CONFIG_WOLFSSL */ + } + + static int tls_opt_ciphersuite_used_get(struct tls_context *context, + void *optval, socklen_t *optlen) + { +- const char *ciph; +- + if (*optlen != sizeof(int)) { + return -EINVAL; + } + +- ciph = mbedtls_ssl_get_ciphersuite(&context->ssl); +- if (ciph == NULL) { +- return -ENOTCONN; ++#if defined(CONFIG_WOLFSSL) ++ { ++ const char *ciph; ++ byte b1, b2; ++ ++ if (context->wssl == NULL) { ++ return -ENOTCONN; ++ } ++ ++ ciph = wolfSSL_get_cipher_name(context->wssl); ++ if (ciph == NULL) { ++ return -ENOTCONN; ++ } ++ ++ int flags = 0; ++ ++ if (wolfSSL_get_cipher_suite_from_name(ciph, &b1, &b2, &flags) != 0) { ++ return -ENOTCONN; ++ } ++ ++ byte cs_arr[2] = { b1, b2 }; ++ *(int *)optval = (int)sys_get_be16(cs_arr); + } ++#else ++ { ++ const char *ciph; + +- *(int *)optval = mbedtls_ssl_get_ciphersuite_id(ciph); ++ ciph = mbedtls_ssl_get_ciphersuite(&context->ssl); ++ if (ciph == NULL) { ++ return -ENOTCONN; ++ } ++ ++ *(int *)optval = mbedtls_ssl_get_ciphersuite_id(ciph); ++ } ++#endif /* CONFIG_WOLFSSL */ + + return 0; + } +@@ -1800,6 +3530,12 @@ static int tls_opt_alpn_list_set(struct tls_context *context, + memcpy(context->options.alpn_list, optval, optlen); + context->options.alpn_list[alpn_cnt] = NULL; + ++#if defined(CONFIG_WOLFSSL) && defined(HAVE_ALPN) ++ if (context->wssl != NULL) { ++ return tls_wolfssl_set_alpn(context); ++ } ++#endif ++ + return 0; + } + +@@ -1846,12 +3582,18 @@ static int tls_opt_dtls_handshake_timeout_set(struct tls_context *context, + context->options.dtls_handshake_timeout_min = *val; + } + +- /* If mbedTLS context already inited, we need to +- * update mbedTLS config for it to take effect ++ /* If context already inited, we need to ++ * update config for it to take effect + */ ++#if defined(CONFIG_WOLFSSL) ++ if (context->wssl != NULL) { ++ return tls_wolfssl_set_dtls_timeouts(context); ++ } ++#else + mbedtls_ssl_conf_handshake_timeout(&context->config, + context->options.dtls_handshake_timeout_min, + context->options.dtls_handshake_timeout_max); ++#endif + + return 0; + } +@@ -2123,18 +3865,43 @@ static int tls_opt_cert_verify_result_get(struct tls_context *context, + return -EINVAL; + } + ++#if defined(CONFIG_WOLFSSL) ++ if (context->wssl == NULL) { ++ return -ENOTCONN; ++ } ++ ++ { ++ uint32_t result = context->verify_result_flags; ++ ++#if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL) ++ /* Fallback for errors that bypass the verify callback. */ ++ if (result == 0 && ++ wolfSSL_get_verify_result(context->wssl) != 0) { ++ result = MBEDTLS_X509_BADCERT_OTHER; ++ } ++#endif ++ *(uint32_t *)optval = result; ++ return 0; ++ } ++#else + *(uint32_t *)optval = mbedtls_ssl_get_verify_result(&context->ssl); + + return 0; ++#endif + } + + static int tls_opt_session_cache_purge_set(struct tls_context *context, + const void *optval, socklen_t optlen) + { +- ARG_UNUSED(context); + ARG_UNUSED(optval); + ARG_UNUSED(optlen); + ++#if defined(CONFIG_WOLFSSL) ++ /* wolfSSL_CTX_flush_sessions ignores ctx and flushes the global cache. */ ++ wolfSSL_CTX_flush_sessions(context->ctx, -1); ++#else ++ ARG_UNUSED(context); ++#endif + tls_session_purge(); + + return 0; +@@ -2155,9 +3922,9 @@ static int tls_opt_peer_verify_set(struct tls_context *context, + + peer_verify = (int *)optval; + +- if (*peer_verify != MBEDTLS_SSL_VERIFY_NONE && +- *peer_verify != MBEDTLS_SSL_VERIFY_OPTIONAL && +- *peer_verify != MBEDTLS_SSL_VERIFY_REQUIRED) { ++ if (*peer_verify != TLS_PEER_VERIFY_NONE && ++ *peer_verify != TLS_PEER_VERIFY_OPTIONAL && ++ *peer_verify != TLS_PEER_VERIFY_REQUIRED) { + return -EINVAL; + } + +@@ -2205,8 +3972,8 @@ static int tls_opt_dtls_role_set(struct tls_context *context, + } + + role = (int *)optval; +- if (*role != MBEDTLS_SSL_IS_CLIENT && +- *role != MBEDTLS_SSL_IS_SERVER) { ++ if (*role != ZTLS_IS_CLIENT && ++ *role != ZTLS_IS_SERVER) { + return -EINVAL; + } + +@@ -2216,6 +3983,22 @@ static int tls_opt_dtls_role_set(struct tls_context *context, + } + + #if defined(CONFIG_NET_SOCKETS_TLS_CERT_VERIFY_CALLBACK) ++#if defined(CONFIG_WOLFSSL) ++/* wolfSSL consumers should use TLS_CERT_VERIFY_CALLBACK_WOLFSSL. */ ++static int tls_opt_cert_verify_callback_set(struct tls_context *context, ++ const void *optval, ++ socklen_t optlen) ++{ ++ ARG_UNUSED(context); ++ ARG_UNUSED(optval); ++ ARG_UNUSED(optlen); ++ ++ NET_ERR("TLS_CERT_VERIFY_CALLBACK is not supported by the wolfSSL " ++ "backend; use TLS_CERT_VERIFY_CALLBACK_WOLFSSL"); ++ ++ return -ENOTSUP; ++} ++#else /* CONFIG_WOLFSSL (mbedTLS backend) */ + static int tls_opt_cert_verify_callback_set(struct tls_context *context, + const void *optval, + socklen_t optlen) +@@ -2239,6 +4022,7 @@ static int tls_opt_cert_verify_callback_set(struct tls_context *context, + + return 0; + } ++#endif /* CONFIG_WOLFSSL */ + #else /* CONFIG_NET_SOCKETS_TLS_CERT_VERIFY_CALLBACK */ + static int tls_opt_cert_verify_callback_set(struct tls_context *context, + const void *optval, +@@ -2251,6 +4035,39 @@ static int tls_opt_cert_verify_callback_set(struct tls_context *context, + } + #endif /* CONFIG_NET_SOCKETS_TLS_CERT_VERIFY_CALLBACK */ + ++#if defined(CONFIG_WOLFSSL_VERIFY_CALLBACK) ++static int tls_opt_cert_verify_callback_wolfssl_set(struct tls_context *context, ++ const void *optval, ++ socklen_t optlen) ++{ ++ struct tls_cert_verify_cb_wolfssl *wolfssl_cb; ++ ++ if (!optval) { ++ return -EINVAL; ++ } ++ ++ if (optlen != sizeof(struct tls_cert_verify_cb_wolfssl)) { ++ return -EINVAL; ++ } ++ ++ wolfssl_cb = (struct tls_cert_verify_cb_wolfssl *)optval; ++ if (wolfssl_cb->cb == NULL) { ++ return -EINVAL; ++ } ++ ++ context->options.cert_verify_wolfssl = *wolfssl_cb; ++ ++ return 0; ++} ++#else ++static int tls_opt_cert_verify_callback_wolfssl_set(struct tls_context *context, ++ const void *optval, ++ socklen_t optlen) ++{ ++ return -ENOPROTOOPT; ++} ++#endif ++ + static int protocol_check(int family, int type, int *proto) + { + if (family != AF_INET && family != AF_INET6) { +@@ -2334,7 +4151,11 @@ int ztls_close_ctx(struct tls_context *ctx, int sock) + /* Try to send close notification. */ + ctx->flags = 0; + ++#if defined(CONFIG_WOLFSSL) ++ /* wolfSSL shutdown handled in tls_release */ ++#else + (void)mbedtls_ssl_close_notify(&ctx->ssl); ++#endif + + err = tls_release(ctx); + ret = zsock_close(ctx->sock); +@@ -2393,6 +4214,32 @@ int ztls_connect_ctx(struct tls_context *ctx, const struct sockaddr *addr, + || (ctx->type == SOCK_DGRAM && ctx->options.dtls_handshake_on_connect) + #endif + ) { ++#if defined(CONFIG_WOLFSSL) ++ ret = tls_wolfssl_init(ctx, false); ++ if (ret < 0) { ++ goto error; ++ } ++ ++ /* Do not use any socket flags during the handshake. */ ++ ctx->flags = 0; ++ ++ tls_session_restore(ctx, addr, addrlen); ++ ++ /* TODO For simplicity, TLS handshake blocks the socket ++ * even for non-blocking socket. ++ */ ++ ret = tls_wolfssl_connect( ++ ctx, K_MSEC(CONFIG_NET_SOCKETS_CONNECT_TIMEOUT)); ++ if (ret < 0) { ++ if ((ret == -EAGAIN) && !is_non_block) { ++ ret = -ETIMEDOUT; ++ } ++ ++ goto error; ++ } ++ ++ tls_session_store(ctx, addr, addrlen); ++#else + ret = tls_mbedtls_init(ctx, false); + if (ret < 0) { + goto error; +@@ -2417,6 +4264,7 @@ int ztls_connect_ctx(struct tls_context *ctx, const struct sockaddr *addr, + } + + tls_session_store(ctx, addr, addrlen); ++#endif /* CONFIG_WOLFSSL */ + } + + return 0; +@@ -2457,6 +4305,25 @@ int ztls_accept_ctx(struct tls_context *parent, struct sockaddr *addr, + + child->sock = sock; + ++#if defined(CONFIG_WOLFSSL) ++ ret = tls_wolfssl_init(child, true); ++ if (ret < 0) { ++ goto error; ++ } ++ ++ child->flags = 0; ++ ++ /* TODO For simplicity, TLS handshake blocks the socket even for ++ * non-blocking socket. ++ */ ++ ret = tls_wolfssl_accept( ++ child, K_MSEC(CONFIG_NET_SOCKETS_CONNECT_TIMEOUT)); ++ if (ret < 0) { ++ goto error; ++ } ++ ++ return fd; ++#else + ret = tls_mbedtls_init(child, true); + if (ret < 0) { + goto error; +@@ -2475,6 +4342,7 @@ int ztls_accept_ctx(struct tls_context *parent, struct sockaddr *addr, + } + + return fd; ++#endif /* CONFIG_WOLFSSL */ + + error: + if (child != NULL) { +@@ -2493,6 +4361,7 @@ error: + return -1; + } + ++#if defined(CONFIG_MBEDTLS) + static ssize_t send_tls(struct tls_context *ctx, const void *buf, + size_t len, int flags) + { +@@ -2546,7 +4415,7 @@ static ssize_t send_tls(struct tls_context *ctx, const void *buf, + /* Block. */ + timeout_ms = timeout_to_ms(&timeout); + ret = wait_for_reason(ctx->sock, timeout_ms, ret); +- if (ret != 0) { ++ if (ret < 0) { + errno = -ret; + break; + } +@@ -2571,8 +4440,85 @@ static ssize_t send_tls(struct tls_context *ctx, const void *buf, + + return -1; + } ++#endif /* CONFIG_MBEDTLS */ + +-#if defined(CONFIG_NET_SOCKETS_ENABLE_DTLS) ++#if defined(CONFIG_WOLFSSL) && defined(CONFIG_NET_SOCKETS_ENABLE_DTLS) ++static ssize_t sendto_dtls_client_wolfssl(struct tls_context *ctx, ++ const void *buf, size_t len, ++ int flags, ++ const struct sockaddr *dest_addr, ++ socklen_t addrlen) ++{ ++ int ret; ++ ++ if (!dest_addr) { ++ /* No address provided, check if we have stored one, ++ * otherwise return error. ++ */ ++ if (ctx->dtls_peer_addrlen == 0) { ++ ret = -EDESTADDRREQ; ++ goto error; ++ } ++ } else if (ctx->dtls_peer_addrlen == 0) { ++ /* Address provided and no peer address stored. */ ++ dtls_peer_address_set(ctx, dest_addr, addrlen); ++ } else if (!dtls_is_peer_addr_valid(ctx, dest_addr, addrlen)) { ++ /* Address provided but it does not match stored one */ ++ ret = -EISCONN; ++ goto error; ++ } ++ ++ if (!ctx->is_initialized) { ++ ret = tls_wolfssl_init(ctx, false); ++ if (ret < 0) { ++ goto error; ++ } ++ } ++ ++ if (!is_handshake_complete(ctx)) { ++ tls_session_restore(ctx, &ctx->dtls_peer_addr, ++ ctx->dtls_peer_addrlen); ++ ++ ret = tls_wolfssl_connect(ctx, K_FOREVER); ++ if (ret < 0) { ++ goto error; ++ } ++ ++ ctx->error = 0; ++ ++ tls_session_store(ctx, &ctx->dtls_peer_addr, ++ ctx->dtls_peer_addrlen); ++ } ++ ++ return send_tls_wolfssl(ctx, buf, len, flags); ++ ++error: ++ errno = -ret; ++ return -1; ++} ++ ++static ssize_t sendto_dtls_server_wolfssl(struct tls_context *ctx, ++ const void *buf, size_t len, ++ int flags, ++ const struct sockaddr *dest_addr, ++ socklen_t addrlen) ++{ ++ /* For DTLS server, verify handshake is done and dest matches peer */ ++ if (!is_handshake_complete(ctx)) { ++ errno = ENOTCONN; ++ return -1; ++ } ++ ++ if (dest_addr && !dtls_is_peer_addr_valid(ctx, dest_addr, addrlen)) { ++ errno = EISCONN; ++ return -1; ++ } ++ ++ return send_tls_wolfssl(ctx, buf, len, flags); ++} ++#endif /* CONFIG_WOLFSSL && CONFIG_NET_SOCKETS_ENABLE_DTLS */ ++ ++#if defined(CONFIG_NET_SOCKETS_ENABLE_DTLS) && defined(CONFIG_MBEDTLS) + static ssize_t sendto_dtls_client(struct tls_context *ctx, const void *buf, + size_t len, int flags, + const struct sockaddr *dest_addr, +@@ -2664,6 +4610,24 @@ ssize_t ztls_sendto_ctx(struct tls_context *ctx, const void *buf, size_t len, + ctx->flags = flags; + + /* TLS */ ++#if defined(CONFIG_WOLFSSL) ++ if (ctx->type == SOCK_STREAM) { ++ return send_tls_wolfssl(ctx, buf, len, flags); ++ } ++ ++#if defined(CONFIG_NET_SOCKETS_ENABLE_DTLS) ++ if (ctx->options.role == ZTLS_IS_SERVER) { ++ return sendto_dtls_server_wolfssl(ctx, buf, len, flags, ++ dest_addr, addrlen); ++ } ++ ++ return sendto_dtls_client_wolfssl(ctx, buf, len, flags, ++ dest_addr, addrlen); ++#else ++ errno = ENOTSUP; ++ return -1; ++#endif /* CONFIG_NET_SOCKETS_ENABLE_DTLS */ ++#else + if (ctx->type == SOCK_STREAM) { + return send_tls(ctx, buf, len, flags); + } +@@ -2680,6 +4644,7 @@ ssize_t ztls_sendto_ctx(struct tls_context *ctx, const void *buf, size_t len, + errno = ENOTSUP; + return -1; + #endif /* CONFIG_NET_SOCKETS_ENABLE_DTLS */ ++#endif /* CONFIG_WOLFSSL */ + } + + static ssize_t dtls_sendmsg_merge_and_send(struct tls_context *ctx, +@@ -2785,6 +4750,7 @@ send_loop: + return tls_sendmsg_loop_and_send(ctx, msg, flags); + } + ++#if defined(CONFIG_MBEDTLS) + static ssize_t recv_tls(struct tls_context *ctx, void *buf, + size_t max_len, int flags) + { +@@ -2861,7 +4827,7 @@ static ssize_t recv_tls(struct tls_context *ctx, void *buf, + ret = wait_for_reason(ctx->sock, timeout_ms, ret); + k_mutex_lock(ctx->lock, K_FOREVER); + +- if (ret == 0) { ++ if (ret >= 0) { + /* Retry. */ + continue; + } +@@ -2884,8 +4850,241 @@ err: + + return recv_len; + } ++#endif /* CONFIG_MBEDTLS */ + +-#if defined(CONFIG_NET_SOCKETS_ENABLE_DTLS) ++#if defined(CONFIG_WOLFSSL) && defined(CONFIG_NET_SOCKETS_ENABLE_DTLS) ++static ssize_t recvfrom_dtls_common_wolfssl(struct tls_context *ctx, void *buf, ++ size_t max_len, int flags, ++ struct sockaddr *src_addr, ++ socklen_t *addrlen) ++{ ++ int ret; ++ bool is_block = is_blocking(ctx->sock, flags); ++ k_timeout_t timeout; ++ k_timepoint_t end; ++ int remaining; ++ bool retry; ++ ++ if (ctx->error != 0) { ++ return -ctx->error; ++ } ++ ++ if (!is_block) { ++ timeout = K_NO_WAIT; ++ } else { ++ timeout = ctx->options.timeout_rx; ++ } ++ ++ end = sys_timepoint_calc(timeout); ++ ++ do { ++ int timeout_ms; ++ ++ retry = false; ++ ret = wolfSSL_read(ctx->wssl, buf, max_len); ++ if (ret < 0) { ++ int err = wolfSSL_get_error(ctx->wssl, ret); ++ ++ if (err == WOLFSSL_ERROR_WANT_READ || ++ err == WOLFSSL_ERROR_WANT_WRITE) { ++ if (!is_block) { ++ return -EAGAIN; ++ } ++ ++ timeout = sys_timepoint_timeout(end); ++ if (K_TIMEOUT_EQ(timeout, K_NO_WAIT)) { ++ return -EAGAIN; ++ } ++ ++ { ++ int timeout_dtls = ++ wolfSSL_dtls_get_current_timeout( ++ ctx->wssl); ++ int timeout_sock = ++ timeout_to_ms(&timeout); ++ ++ if (timeout_sock == SYS_FOREVER_MS) { ++ timeout_ms = MAX(timeout_dtls, ++ timeout_sock); ++ } else { ++ timeout_ms = MIN(timeout_dtls, ++ timeout_sock); ++ } ++ } ++ ++ k_mutex_unlock(ctx->lock); ++ ret = wait_for_reason(ctx->sock, timeout_ms, err); ++ k_mutex_lock(ctx->lock, K_FOREVER); ++ ++ if (ret >= 0) { ++ retry = true; ++ continue; ++ } ++ ++ return ret; ++ } ++ ++ if (err == SOCKET_PEER_CLOSED_E || ++ err == WOLFSSL_ERROR_ZERO_RETURN) { ++ return -ENOTCONN; ++ } ++ ++ /* Server BUFFER_ERROR: a prior peek absorbed a fatal ++ * alert and reset the SSL object. Surface EAGAIN so ++ * the server can wait for the next session. ++ */ ++ if (err == BUFFER_ERROR && ++ ctx->options.role == ZTLS_IS_SERVER) { ++ return -EAGAIN; ++ } ++ ++ return -EIO; ++ } ++ ++ if (ret == 0) { ++ return 0; ++ } ++ ++ if (src_addr && addrlen) { ++ dtls_peer_address_get(ctx, src_addr, addrlen); ++ } ++ ++ remaining = wolfSSL_pending(ctx->wssl); ++ ++ /* No more data in the datagram, or dummy read. */ ++ if ((remaining == 0) || (max_len == 0)) { ++ return ret; ++ } ++ ++ if (flags & ZSOCK_MSG_TRUNC) { ++ ret += remaining; ++ } ++ ++ /* Always drain remaining datagram data */ ++ while (remaining > 0) { ++ byte dummy[128]; ++ int to_read = MIN(remaining, sizeof(dummy)); ++ int r = wolfSSL_read(ctx->wssl, dummy, to_read); ++ ++ if (r <= 0) { ++ NET_ERR("Error while flushing the rest of the" ++ " datagram, err %d", r); ++ ret = -EIO; ++ break; ++ } ++ remaining -= r; ++ } ++ ++ return ret; ++ } while (retry); ++ ++ return -EAGAIN; ++} ++ ++static ssize_t recvfrom_dtls_client_wolfssl(struct tls_context *ctx, void *buf, ++ size_t max_len, int flags, ++ struct sockaddr *src_addr, ++ socklen_t *addrlen) ++{ ++ int ret; ++ ++ if (!is_handshake_complete(ctx)) { ++ ret = -ENOTCONN; ++ goto error; ++ } ++ ++ ret = recvfrom_dtls_common_wolfssl(ctx, buf, max_len, flags, ++ src_addr, addrlen); ++ if (ret >= 0) { ++ return ret; ++ } ++ ++ if (ret == -ENOTCONN) { ++ tls_wolfssl_reset(ctx); ++ ctx->error = ENOTCONN; ++ goto error; ++ } ++ if (ret == -EAGAIN) { ++ goto error; ++ } ++ ++ tls_wolfssl_reset(ctx); ++ ret = -ECONNABORTED; ++ ++error: ++ errno = -ret; ++ return -1; ++} ++ ++static ssize_t recvfrom_dtls_server_wolfssl(struct tls_context *ctx, void *buf, ++ size_t max_len, int flags, ++ struct sockaddr *src_addr, ++ socklen_t *addrlen) ++{ ++ int ret; ++ bool repeat; ++ k_timeout_t timeout; ++ ++ if (!is_blocking(ctx->sock, flags)) { ++ timeout = K_NO_WAIT; ++ } else { ++ timeout = ctx->options.timeout_rx; ++ } ++ ++ do { ++ repeat = false; ++ ++ if (!ctx->is_initialized) { ++ ret = tls_wolfssl_init(ctx, true); ++ if (ret < 0) { ++ goto error; ++ } ++ } ++ ++ if (!is_handshake_complete(ctx)) { ++ ret = tls_wolfssl_accept(ctx, timeout); ++ if (ret < 0) { ++ /* In case of EAGAIN, just exit. */ ++ if (ret == -EAGAIN) { ++ break; ++ } ++ ++ tls_wolfssl_reset(ctx); ++ repeat = true; ++ continue; ++ } ++ ++ /* Server socket ready to use again. */ ++ ctx->error = 0; ++ } ++ ++ ret = recvfrom_dtls_common_wolfssl(ctx, buf, max_len, flags, ++ src_addr, addrlen); ++ if (ret >= 0) { ++ return ret; ++ } ++ ++ if (ret == -ENOTCONN) { ++ tls_wolfssl_reset(ctx); ++ repeat = true; ++ continue; ++ } ++ if (ret == -EAGAIN) { ++ break; ++ } ++ ++ tls_wolfssl_reset(ctx); ++ ret = -ECONNABORTED; ++ break; ++ } while (repeat); ++ ++error: ++ errno = -ret; ++ return -1; ++} ++#endif /* CONFIG_WOLFSSL && CONFIG_NET_SOCKETS_ENABLE_DTLS */ ++ ++#if defined(CONFIG_NET_SOCKETS_ENABLE_DTLS) && defined(CONFIG_MBEDTLS) + static ssize_t recvfrom_dtls_common(struct tls_context *ctx, void *buf, + size_t max_len, int flags, + struct sockaddr *src_addr, +@@ -2944,7 +5143,7 @@ static ssize_t recvfrom_dtls_common(struct tls_context *ctx, void *buf, + ret = wait_for_reason(ctx->sock, timeout_ms, ret); + k_mutex_lock(ctx->lock, K_FOREVER); + +- if (ret == 0) { ++ if (ret >= 0) { + /* Retry. */ + continue; + } else { +@@ -3175,6 +5374,25 @@ ssize_t ztls_recvfrom_ctx(struct tls_context *ctx, void *buf, size_t max_len, + + ctx->flags = flags; + ++#if defined(CONFIG_WOLFSSL) ++ /* TLS */ ++ if (ctx->type == SOCK_STREAM) { ++ return recv_tls_wolfssl(ctx, buf, max_len, flags); ++ } ++ ++#if defined(CONFIG_NET_SOCKETS_ENABLE_DTLS) ++ if (ctx->options.role == ZTLS_IS_SERVER) { ++ return recvfrom_dtls_server_wolfssl(ctx, buf, max_len, flags, ++ src_addr, addrlen); ++ } ++ ++ return recvfrom_dtls_client_wolfssl(ctx, buf, max_len, flags, ++ src_addr, addrlen); ++#else ++ errno = ENOTSUP; ++ return -1; ++#endif /* CONFIG_NET_SOCKETS_ENABLE_DTLS */ ++#else + /* TLS */ + if (ctx->type == SOCK_STREAM) { + return recv_tls(ctx, buf, max_len, flags); +@@ -3193,18 +5411,25 @@ ssize_t ztls_recvfrom_ctx(struct tls_context *ctx, void *buf, size_t max_len, + errno = ENOTSUP; + return -1; + #endif /* CONFIG_NET_SOCKETS_ENABLE_DTLS */ ++#endif /* CONFIG_WOLFSSL */ + } + + static int ztls_poll_prepare_pollin(struct tls_context *ctx) + { +- /* If there already is mbedTLS data to read, there is no ++ /* If there already is TLS data to read, there is no + * need to set the k_poll_event object. Return EALREADY + * so we won't block in the k_poll. + */ + if (!ctx->is_listening) { ++#if defined(CONFIG_WOLFSSL) ++ if (ctx->wssl != NULL && wolfSSL_pending(ctx->wssl) > 0) { ++ return -EALREADY; ++ } ++#else + if (mbedtls_ssl_get_bytes_avail(&ctx->ssl) > 0) { + return -EALREADY; + } ++#endif + } + + return 0; +@@ -3225,7 +5450,7 @@ static int ztls_poll_prepare_ctx(struct tls_context *ctx, + * it actually starts to poll for data. + */ + if ((pfd->events & ZSOCK_POLLIN) && (ctx->type == SOCK_DGRAM) && +- (ctx->options.role == MBEDTLS_SSL_IS_CLIENT) && ++ (ctx->options.role == ZTLS_IS_CLIENT) && + !is_handshake_complete(ctx)) { + (*pev)->obj = &ctx->tls_established; + (*pev)->type = K_POLL_TYPE_SEM_AVAILABLE; +@@ -3273,6 +5498,79 @@ static int ztls_socket_data_check(struct tls_context *ctx) + { + int ret; + ++#if defined(CONFIG_WOLFSSL) ++ if (ctx->type == SOCK_STREAM) { ++ if (!ctx->is_initialized) { ++ return -ENOTCONN; ++ } ++ } ++#if defined(CONFIG_NET_SOCKETS_ENABLE_DTLS) ++ else { ++ if (!ctx->is_initialized) { ++ bool is_server = ctx->options.role == ZTLS_IS_SERVER; ++ ++ ret = tls_wolfssl_init(ctx, is_server); ++ if (ret < 0) { ++ /* Match mbedTLS arm — collapse to -ENOMEM. */ ++ return -ENOMEM; ++ } ++ } ++ ++ if (!is_handshake_complete(ctx)) { ++ if (ctx->options.role == ZTLS_IS_SERVER) { ++ ret = tls_wolfssl_accept(ctx, K_NO_WAIT); ++ } else { ++ ret = tls_wolfssl_connect(ctx, K_NO_WAIT); ++ } ++ ++ if (ret < 0) { ++ if (ret == -EAGAIN) { ++ return 0; ++ } ++ ++ tls_wolfssl_reset(ctx); ++ return 0; ++ } ++ ++ /* Socket ready to use again. */ ++ ctx->error = 0; ++ return 0; ++ } ++ } ++#endif /* CONFIG_NET_SOCKETS_ENABLE_DTLS */ ++ ++ ctx->flags = ZSOCK_MSG_DONTWAIT; ++ ++ { ++ byte dummy[1]; ++ ++ ret = wolfSSL_peek(ctx->wssl, dummy, sizeof(dummy)); ++ if (ret < 0) { ++ int err = wolfSSL_get_error(ctx->wssl, ret); ++ ++ if (err == SOCKET_PEER_CLOSED_E || ++ err == WOLFSSL_ERROR_ZERO_RETURN) { ++ if (ctx->type == SOCK_DGRAM) { ++ tls_wolfssl_reset(ctx); ++ } else { ++ ctx->session_closed = true; ++ } ++ return -ENOTCONN; ++ } ++ ++ if (wolfSSL_want_read(ctx->wssl) || ++ wolfSSL_want_write(ctx->wssl)) { ++ return 0; ++ } ++ ++ NET_ERR("TLS data check error: %d", err); ++ tls_wolfssl_reset(ctx); ++ return -ECONNABORTED; ++ } ++ } ++ ++ return wolfSSL_pending(ctx->wssl); ++#else + if (ctx->type == SOCK_STREAM) { + if (!ctx->is_initialized) { + return -ENOTCONN; +@@ -3317,11 +5615,6 @@ static int ztls_socket_data_check(struct tls_context *ctx) + ret = mbedtls_ssl_read(&ctx->ssl, NULL, 0); + if (ret < 0) { + if (ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) { +- /* Don't reset the context for STREAM socket - the +- * application needs to reopen the socket anyway, and +- * resetting the context would result in an error instead +- * of 0 in a consecutive recv() call. +- */ + if (ctx->type == SOCK_DGRAM) { + ret = tls_mbedtls_reset(ctx); + if (ret != 0) { +@@ -3341,16 +5634,12 @@ static int ztls_socket_data_check(struct tls_context *ctx) + + NET_ERR("TLS data check error: -%x", -ret); + +- /* MbedTLS API documentation requires session to +- * be reset in other error cases +- */ + if (tls_mbedtls_reset(ctx) != 0) { + return -ENOMEM; + } + + #if defined(CONFIG_NET_SOCKETS_ENABLE_DTLS) + if (ret == MBEDTLS_ERR_SSL_TIMEOUT && ctx->type == SOCK_DGRAM) { +- /* DTLS timeout interpreted as closing of connection. */ + return -ENOTCONN; + } + #endif +@@ -3358,6 +5647,7 @@ static int ztls_socket_data_check(struct tls_context *ctx) + } + + return mbedtls_ssl_get_bytes_avail(&ctx->ssl); ++#endif /* CONFIG_WOLFSSL */ + } + + static int ztls_poll_update_pollin(int fd, struct tls_context *ctx, +@@ -3367,10 +5657,17 @@ static int ztls_poll_update_pollin(int fd, struct tls_context *ctx, + + if (!ctx->is_listening) { + /* Already had TLS data to read on socket. */ ++#if defined(CONFIG_WOLFSSL) ++ if (ctx->wssl != NULL && wolfSSL_pending(ctx->wssl) > 0) { ++ pfd->revents |= ZSOCK_POLLIN; ++ goto next; ++ } ++#else + if (mbedtls_ssl_get_bytes_avail(&ctx->ssl) > 0) { + pfd->revents |= ZSOCK_POLLIN; + goto next; + } ++#endif + } + + if (ctx->type == SOCK_STREAM) { +@@ -3394,6 +5691,7 @@ static int ztls_poll_update_pollin(int fd, struct tls_context *ctx, + } + #endif + ret = ztls_socket_data_check(ctx); ++ + if (ret == -ENOTCONN || (pfd->revents & ZSOCK_POLLHUP)) { + /* Datagram does not return 0 on consecutive recv, but an error + * code, hence clear POLLIN. +@@ -3504,7 +5802,7 @@ static bool poll_offload_dtls_client_retry(struct tls_context *ctx, + * reports that data is ready. + */ + if ((ctx->type != SOCK_DGRAM) || +- (ctx->options.role != MBEDTLS_SSL_IS_CLIENT)) { ++ (ctx->options.role != ZTLS_IS_CLIENT)) { + return false; + } + +@@ -3517,13 +5815,13 @@ static bool poll_offload_dtls_client_retry(struct tls_context *ctx, + pfd->revents &= ~ZSOCK_POLLIN; + return true; + } else if (!is_handshake_complete(ctx)) { +- uint8_t byte; ++ uint8_t b; + int ret; + + /* Handshake didn't start yet - just drop the incoming data - + * it's the client who should initiate the handshake. + */ +- ret = zsock_recv(ctx->sock, &byte, sizeof(byte), ++ ret = zsock_recv(ctx->sock, &b, sizeof(b), + ZSOCK_MSG_DONTWAIT); + if (ret < 0) { + pfd->revents |= ZSOCK_POLLERR; +@@ -3842,6 +6140,10 @@ int ztls_setsockopt_ctx(struct tls_context *ctx, int level, int optname, + err = tls_opt_cert_verify_callback_set(ctx, optval, optlen); + break; + ++ case TLS_CERT_VERIFY_CALLBACK_WOLFSSL: ++ err = tls_opt_cert_verify_callback_wolfssl_set(ctx, optval, optlen); ++ break; ++ + #if defined(CONFIG_NET_SOCKETS_ENABLE_DTLS) + case TLS_DTLS_HANDSHAKE_TIMEOUT_MIN: + err = tls_opt_dtls_handshake_timeout_set(ctx, optval, +@@ -3888,6 +6190,21 @@ out: + } + + #if defined(CONFIG_NET_TEST) ++#if defined(CONFIG_WOLFSSL) ++ ++WOLFSSL *ztls_get_wolfssl_context(int fd) ++{ ++ struct tls_context *ctx; ++ ++ ctx = zvfs_get_fd_obj(fd, (const struct fd_op_vtable *) ++ &tls_sock_fd_op_vtable, EBADF); ++ if (ctx == NULL) { ++ return NULL; ++ } ++ ++ return ctx->wssl; ++} ++#elif defined(CONFIG_MBEDTLS) + mbedtls_ssl_context *ztls_get_mbedtls_ssl_context(int fd) + { + struct tls_context *ctx; +@@ -3900,17 +6217,18 @@ mbedtls_ssl_context *ztls_get_mbedtls_ssl_context(int fd) + + return &ctx->ssl; + } ++#endif + #endif /* CONFIG_NET_TEST */ + +-static ssize_t tls_sock_read_vmeth(void *obj, void *buffer, size_t count) ++static ssize_t tls_sock_read_vmeth(void *obj, void *buf, size_t count) + { +- return ztls_recvfrom_ctx(obj, buffer, count, 0, NULL, 0); ++ return ztls_recvfrom_ctx(obj, buf, count, 0, NULL, 0); + } + +-static ssize_t tls_sock_write_vmeth(void *obj, const void *buffer, ++static ssize_t tls_sock_write_vmeth(void *obj, const void *buf, + size_t count) + { +- return ztls_sendto_ctx(obj, buffer, count, 0, NULL, 0); ++ return ztls_sendto_ctx(obj, buf, count, 0, NULL, 0); + } + + static int tls_sock_ioctl_vmeth(void *obj, unsigned int request, va_list args) +diff --git a/subsys/random/Kconfig b/subsys/random/Kconfig +index 0e455630471..6331218e713 100644 +--- a/subsys/random/Kconfig ++++ b/subsys/random/Kconfig +@@ -119,9 +119,9 @@ config HARDWARE_DEVICE_CS_GENERATOR + + config CTR_DRBG_CSPRNG_GENERATOR + bool "Use CTR-DRBG CSPRNG" +- depends on MBEDTLS ++ depends on WOLFSSL || MBEDTLS + depends on ENTROPY_HAS_DRIVER +- select MBEDTLS_CIPHER_AES_ENABLED ++ select MBEDTLS_CIPHER_AES_ENABLED if MBEDTLS + help + Enables the CTR-DRBG pseudo-random number generator. This CSPRNG + shall use the entropy API for an initialization seed. The CTR-DRBG +diff --git a/subsys/random/random_ctr_drbg.c b/subsys/random/random_ctr_drbg.c +index 25f563e3040..a61fd2134d3 100644 +--- a/subsys/random/random_ctr_drbg.c ++++ b/subsys/random/random_ctr_drbg.c +@@ -10,6 +10,7 @@ + #include + #include + ++#if defined(CONFIG_MBEDTLS) + #if !defined(CONFIG_MBEDTLS_CFG_FILE) + #include "mbedtls/config.h" + #else +@@ -17,6 +18,15 @@ + #endif /* CONFIG_MBEDTLS_CFG_FILE */ + #include + ++#elif defined(CONFIG_WOLFSSL) ++#ifndef WOLFSSL_USER_SETTINGS ++#include ++#endif ++#include ++#include ++ ++#endif /* CONFIG_MBEDTLS */ ++ + /* + * entropy_dev is initialized at runtime to allow first time initialization + * of the ctr_drbg engine. +@@ -26,6 +36,7 @@ static const unsigned char drbg_seed[] = CONFIG_CS_CTR_DRBG_PERSONALIZATION; + static bool ctr_initialised; + static K_MUTEX_DEFINE(ctr_lock); + ++#if defined(CONFIG_MBEDTLS) + static mbedtls_ctr_drbg_context ctr_ctx; + + static int ctr_drbg_entropy_func(void *ctx, unsigned char *buf, size_t len) +@@ -33,6 +44,17 @@ static int ctr_drbg_entropy_func(void *ctx, unsigned char *buf, size_t len) + return entropy_get_entropy(entropy_dev, (void *)buf, len); + } + ++#elif defined(CONFIG_WOLFSSL) ++ ++static WC_RNG ctr_ctx; ++ ++static int ctr_drbg_entropy_cb(OS_Seed* os, byte* seed, word32 sz) ++{ ++ return entropy_get_entropy(entropy_dev, (void *)seed, (size_t)sz); ++} ++ ++#endif /* CONFIG_MBEDTLS */ ++ + static int ctr_drbg_initialize(void) + { + int ret; +@@ -44,6 +66,7 @@ static int ctr_drbg_initialize(void) + return -ENODEV; + } + ++#if defined(CONFIG_MBEDTLS) + mbedtls_ctr_drbg_init(&ctr_ctx); + + ret = mbedtls_ctr_drbg_seed(&ctr_ctx, +@@ -57,6 +80,15 @@ static int ctr_drbg_initialize(void) + return -EIO; + } + ++#elif defined(CONFIG_WOLFSSL) ++ ret = wc_SetSeed_Cb(ctr_drbg_entropy_cb); ++ if (ret != 0) ++ return -EIO; ++ ++ ret = wc_InitRngNonce_ex(&ctr_ctx, (byte *)drbg_seed, sizeof(drbg_seed), NULL, 0); ++ if (ret != 0) ++ return -EIO; ++#endif + ctr_initialised = true; + return 0; + } +@@ -76,8 +108,15 @@ int z_impl_sys_csrand_get(void *dst, uint32_t outlen) + } + } + ++#if defined(CONFIG_MBEDTLS) + ret = mbedtls_ctr_drbg_random(&ctr_ctx, (unsigned char *)dst, outlen); + ++#elif defined(CONFIG_WOLFSSL) ++ ++ ret = wc_RNG_GenerateBlock(&ctr_ctx, (byte *)dst, (word32)outlen); ++ ++#endif ++ + end: + k_mutex_unlock(&ctr_lock); + +diff --git a/tests/net/socket/tls/overlay-wolfssl.conf b/tests/net/socket/tls/overlay-wolfssl.conf +new file mode 100644 +index 00000000000..8da467dbf89 +--- /dev/null ++++ b/tests/net/socket/tls/overlay-wolfssl.conf +@@ -0,0 +1,21 @@ ++# Copyright (c) 2025 wolfSSL Inc. ++# SPDX-License-Identifier: Apache-2.0 ++ ++CONFIG_MBEDTLS=n ++CONFIG_POSIX_API=y ++CONFIG_POSIX_TIMERS=y ++CONFIG_POSIX_THREADS=y ++CONFIG_WOLFSSL=y ++CONFIG_WOLFSSL_DTLS=y ++CONFIG_COMMON_LIBC_MALLOC_ARENA_SIZE=100000 ++CONFIG_WOLFSSL_ALPN=y ++CONFIG_WOLFSSL_PSK=y ++CONFIG_MBEDTLS_ENABLE_HEAP=n ++CONFIG_MBEDTLS_KEY_EXCHANGE_PSK_ENABLED=n ++CONFIG_MBEDTLS_HASH_ALL_ENABLED=n ++CONFIG_MBEDTLS_CMAC=n ++CONFIG_MBEDTLS_SSL_ALPN=n ++CONFIG_WOLFSSL_TLS_VERSION_1_3=y ++CONFIG_WOLFSSL_SESSION_EXPORT=y ++CONFIG_WOLFSSL_KEEP_PEER_CERT=y ++CONFIG_WOLFSSL_ALWAYS_VERIFY_CB=y +diff --git a/tests/net/socket/tls/prj.conf b/tests/net/socket/tls/prj.conf +index 24058c31dd7..be55d4935f2 100644 +--- a/tests/net/socket/tls/prj.conf ++++ b/tests/net/socket/tls/prj.conf +@@ -50,3 +50,5 @@ CONFIG_MBEDTLS_HEAP_SIZE=18000 + CONFIG_MBEDTLS_KEY_EXCHANGE_PSK_ENABLED=y + CONFIG_MBEDTLS_HASH_ALL_ENABLED=y + CONFIG_MBEDTLS_CMAC=y ++CONFIG_MBEDTLS_SSL_ALPN=y ++CONFIG_NET_SOCKETS_TLS_MAX_APP_PROTOCOLS=3 +diff --git a/tests/net/socket/tls/src/main.c b/tests/net/socket/tls/src/main.c +index 8186ba7dbe9..e620aafb3b3 100644 +--- a/tests/net/socket/tls/src/main.c ++++ b/tests/net/socket/tls/src/main.c +@@ -12,15 +12,48 @@ LOG_MODULE_REGISTER(net_test, CONFIG_NET_SOCKETS_LOG_LEVEL); + #include + #include + #include ++#include ++ ++#if defined(CONFIG_WOLFSSL) ++#ifndef WOLFSSL_USER_SETTINGS ++#include ++#endif ++#include ++WOLFSSL *ztls_get_wolfssl_context(int fd); ++int SendAlert(WOLFSSL *ssl, int severity, int type); ++#endif ++ ++#if defined(CONFIG_MBEDTLS) + #include ++mbedtls_ssl_context *ztls_get_mbedtls_ssl_context(int fd); ++#endif + + #include "../../socket_helpers.h" + ++static const int cipher_list_psk[] = { ++ TLS_PSK_WITH_AES_128_CBC_SHA, ++ TLS_PSK_WITH_AES_256_CBC_SHA ++}; ++ ++/* Test test_set_ciphersuites() assumes [0] of this list is the ++ * cipher that will be negotiated */ ++static const int cipher_list_psk2[] = { ++ TLS_PSK_WITH_AES_128_CBC_SHA, ++}; ++ ++static const int cipher_list_psk3[] = { ++ TLS_PSK_WITH_AES_256_CBC_SHA, ++}; ++ + #define TEST_STR_SMALL "test" + + #define MY_IPV4_ADDR "127.0.0.1" + #define MY_IPV6_ADDR "::1" + ++#ifndef MY_DEFLT_HOSTNAME ++#define MY_DEFLT_HOSTNAME "localhost" ++#endif ++ + #define ANY_PORT 0 + #define SERVER_PORT 4242 + +@@ -118,6 +151,20 @@ static void test_connect(int sock, struct sockaddr *addr, socklen_t addrlen) + } + } + ++static void test_connect_err(int sock, struct sockaddr *addr, socklen_t addrlen) ++{ ++ k_yield(); ++ ++ zassert_equal(zsock_connect(sock, addr, addrlen), ++ -1, ++ "connect expected to fail but succeeded"); ++ ++ if (IS_ENABLED(CONFIG_NET_TC_THREAD_PREEMPTIVE)) { ++ /* Let the connection proceed */ ++ k_yield(); ++ } ++} ++ + static void test_send(int sock, const void *buf, size_t len, int flags) + { + zassert_equal(zsock_send(sock, buf, len, flags), +@@ -149,6 +196,15 @@ static void test_accept(int sock, int *new_sock, struct sockaddr *addr, + zassert_true(*new_sock >= 0, "accept failed"); + } + ++static void test_accept_err(int sock, int *new_sock, struct sockaddr *addr, ++ socklen_t *addrlen) ++{ ++ zassert_not_null(new_sock, "null newsock"); ++ ++ *new_sock = zsock_accept(sock, addr, addrlen); ++ zassert_true(*new_sock < 0, "accept expected to fail but succeeded"); ++} ++ + static void test_shutdown(int sock, int how) + { + zassert_equal(zsock_shutdown(sock, how), +@@ -290,6 +346,16 @@ static void client_connect_work_handler(struct k_work *work) + sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6)); + } + ++static void client_connect_work_handler_err(struct k_work *work) ++{ ++ struct k_work_delayable *dwork = k_work_delayable_from_work(work); ++ struct connect_data *data = ++ CONTAINER_OF(dwork, struct connect_data, work); ++ ++ test_connect_err(data->sock, data->addr, data->addr->sa_family == AF_INET ? ++ sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6)); ++} ++ + static void dtls_client_connect_send_work_handler(struct k_work *work) + { + struct k_work_delayable *dwork = k_work_delayable_from_work(work); +@@ -407,6 +473,71 @@ static void test_prepare_dtls_connection(sa_family_t family) + test_work_wait(&test_data.work); + } + ++/* Function pointer type for function to be called after socket creation but ++ * before any bind/listen/connect operations */ ++typedef void (*tls_pre_cb)(void); ++/* Function pointer type for function to be used to perform client work ++ * instead of client_connect_work_handler() */ ++typedef void (*client_work_func)(struct k_work *work); ++ ++static void test_prepare_tls_connection_ex(sa_family_t family, bool accept_err, ++ tls_pre_cb cb, client_work_func cw) ++{ ++ struct sockaddr c_saddr; ++ struct sockaddr s_saddr; ++ socklen_t exp_addrlen = family == AF_INET6 ? ++ sizeof(struct sockaddr_in6) : ++ sizeof(struct sockaddr_in); ++ struct sockaddr addr; ++ socklen_t addrlen = sizeof(addr); ++ struct connect_data test_data; ++ ++ if (family == AF_INET6) { ++ prepare_sock_tls_v6(MY_IPV6_ADDR, ANY_PORT, &c_sock, ++ (struct sockaddr_in6 *)&c_saddr, ++ IPPROTO_TLS_1_2); ++ prepare_sock_tls_v6(MY_IPV6_ADDR, ANY_PORT, &s_sock, ++ (struct sockaddr_in6 *)&s_saddr, ++ IPPROTO_TLS_1_2); ++ } else { ++ prepare_sock_tls_v4(MY_IPV4_ADDR, ANY_PORT, &c_sock, ++ (struct sockaddr_in *)&c_saddr, ++ IPPROTO_TLS_1_2); ++ prepare_sock_tls_v4(MY_IPV4_ADDR, ANY_PORT, &s_sock, ++ (struct sockaddr_in *)&s_saddr, ++ IPPROTO_TLS_1_2); ++ } ++ ++ if (NULL != cb) { ++ cb(); ++ } ++ ++ test_config_psk(s_sock, c_sock); ++ ++ test_bind(s_sock, &s_saddr, exp_addrlen); ++ test_listen(s_sock); ++ ++ /* Helper work for the connect operation - need to handle client/server ++ * in parallel due to handshake. ++ */ ++ test_data.sock = c_sock; ++ test_data.addr = &s_saddr; ++ if (cw != NULL) { ++ k_work_init_delayable(&test_data.work, cw); ++ } else { ++ k_work_init_delayable(&test_data.work, client_connect_work_handler); ++ } ++ test_work_reschedule(&test_data.work, K_NO_WAIT); ++ ++ if (accept_err == true) { ++ test_accept_err(s_sock, &new_sock, &addr, &addrlen); ++ } else { ++ test_accept(s_sock, &new_sock, &addr, &addrlen); ++ zassert_equal(addrlen, exp_addrlen, "Wrong addrlen"); ++ } ++ test_work_wait(&test_data.work); ++} ++ + ZTEST(net_socket_tls, test_v4_msg_waitall) + { + struct test_msg_waitall_data test_data = { +@@ -537,6 +668,7 @@ static void send_work_handler(struct k_work *work) + + void test_msg_trunc(sa_family_t family) + { ++#if defined(CONFIG_NET_SOCKETS_ENABLE_DTLS) + int rv; + uint8_t rx_buf[sizeof(TEST_STR_SMALL) - 1]; + struct send_data test_data = { +@@ -571,6 +703,9 @@ void test_msg_trunc(sa_family_t family) + + /* Small delay for the final alert exchange */ + k_msleep(10); ++#else ++ ztest_test_skip(); ++#endif + } + + ZTEST(net_socket_tls, test_v4_msg_trunc) +@@ -665,18 +800,26 @@ static void test_dtls_sendmsg_no_buf(sa_family_t family) + + ZTEST(net_socket_tls, test_v4_dtls_sendmsg_no_buf) + { ++#if defined(CONFIG_NET_SOCKETS_ENABLE_DTLS) + if (CONFIG_NET_SOCKETS_DTLS_SENDMSG_BUF_SIZE > 0) { + ztest_test_skip(); + } ++#else ++ ztest_test_skip(); ++#endif + + test_dtls_sendmsg_no_buf(AF_INET); + } + + ZTEST(net_socket_tls, test_v6_dtls_sendmsg_no_buf) + { ++#if defined(CONFIG_NET_SOCKETS_ENABLE_DTLS) + if (CONFIG_NET_SOCKETS_DTLS_SENDMSG_BUF_SIZE > 0) { + ztest_test_skip(); + } ++#else ++ ztest_test_skip(); ++#endif + + test_dtls_sendmsg_no_buf(AF_INET6); + } +@@ -785,18 +928,22 @@ static void test_dtls_sendmsg(sa_family_t family) + + ZTEST(net_socket_tls, test_v4_dtls_sendmsg) + { +- if (CONFIG_NET_SOCKETS_DTLS_SENDMSG_BUF_SIZE == 0) { +- ztest_test_skip(); +- } ++#if !defined(CONFIG_NET_SOCKETS_ENABLE_DTLS) ++ ztest_test_skip(); ++#elif CONFIG_NET_SOCKETS_DTLS_SENDMSG_BUF_SIZE <= 0 ++ ztest_test_skip(); ++#endif + + test_dtls_sendmsg(AF_INET); + } + + ZTEST(net_socket_tls, test_v6_dtls_sendmsg) + { +- if (CONFIG_NET_SOCKETS_DTLS_SENDMSG_BUF_SIZE == 0) { +- ztest_test_skip(); +- } ++#if !defined(CONFIG_NET_SOCKETS_ENABLE_DTLS) ++ ztest_test_skip(); ++#elif CONFIG_NET_SOCKETS_DTLS_SENDMSG_BUF_SIZE <= 0 ++ ztest_test_skip(); ++#endif + + test_dtls_sendmsg(AF_INET6); + } +@@ -1165,7 +1312,11 @@ ZTEST(net_socket_tls, test_recv_eof_on_close) + k_sleep(TCP_TEARDOWN_TIMEOUT); + } + ++#if defined(CONFIG_WOLFSSL) ++#define TLS_RECORD_OVERHEAD 29 ++#else + #define TLS_RECORD_OVERHEAD 81 ++#endif + + ZTEST(net_socket_tls, test_send_non_block) + { +@@ -1316,7 +1467,7 @@ ZTEST(net_socket_tls, test_send_on_close) + new_sock = -1; + + /* Small delay for packets to propagate. */ +- k_msleep(10); ++ k_msleep(20); + + /* Verify send() reports an error after connection is closed. */ + ret = zsock_send(c_sock, TEST_STR_SMALL, strlen(TEST_STR_SMALL), 0); +@@ -1338,7 +1489,7 @@ ZTEST(net_socket_tls, test_send_on_close) + new_sock = -1; + + /* Small delay for packets to propagate. */ +- k_msleep(10); ++ k_msleep(20); + + /* Graceful connection close should be reported first. */ + ret = zsock_recv(c_sock, rx_buf, sizeof(rx_buf), 0); +@@ -1542,6 +1693,423 @@ ZTEST(net_socket_tls, test_send_while_recv) + k_sleep(TCP_TEARDOWN_TIMEOUT); + } + ++void tls_set_cs_cb(void) ++{ ++ int ret = zsock_setsockopt(s_sock, SOL_TLS, TLS_CIPHERSUITE_LIST, ++ (void *)cipher_list_psk, sizeof(cipher_list_psk)); ++ zassert_equal(ret, 0, "Unable to set ciphersuites on server"); ++ ret = zsock_setsockopt(c_sock, SOL_TLS, TLS_CIPHERSUITE_LIST, ++ (void *)cipher_list_psk2, sizeof(cipher_list_psk2)); ++ zassert_equal(ret, 0, "Unable to set ciphersuites on client"); ++} ++ ++void tls_set_cs_mismatch_cb(void) ++{ ++ /* Set mismatched ciphersuites, should not connect */ ++ int ret = zsock_setsockopt(c_sock, SOL_TLS, TLS_CIPHERSUITE_LIST, ++ (void *)cipher_list_psk2, sizeof(cipher_list_psk2)); ++ zassert_equal(ret, 0, "Unable to set ciphersuites on client"); ++ ret = zsock_setsockopt(s_sock, SOL_TLS, TLS_CIPHERSUITE_LIST, ++ (void *)cipher_list_psk3, sizeof(cipher_list_psk3)); ++ zassert_equal(ret, 0, "Unable to set ciphersuites on server"); ++} ++ ++ZTEST(net_socket_tls, test_set_ciphersuites) ++{ ++#define TLS_CS_TEST_MAX_CS_NUM 3 ++ int ret; ++ uint8_t rx_buf[sizeof(TEST_STR_SMALL) - 1] = { 0 }; ++ struct send_data test_data = { ++ .data = TEST_STR_SMALL, ++ .datalen = sizeof(TEST_STR_SMALL) - 1 ++ }; ++ int ciphersuites[TLS_CS_TEST_MAX_CS_NUM]; ++ int cs_len = sizeof(ciphersuites); ++ int curr_cipher = 0; ++ int cc_len = sizeof(int); ++ int i; ++ ++ for (i = 0; i < TLS_CS_TEST_MAX_CS_NUM; i++) { ++ ciphersuites[i] = 0; ++ } ++ ++ test_prepare_tls_connection_ex(AF_INET, false, tls_set_cs_cb, NULL); ++ ++ /* Verify the ciphersuite list is what we set for server */ ++ ret = zsock_getsockopt(s_sock, SOL_TLS, TLS_CIPHERSUITE_LIST, ++ (void *)ciphersuites, (socklen_t *)&cs_len); ++ zassert_equal(ret, 0, "Unable to get ciphersuites for server"); ++ zassert_equal(cs_len, sizeof(cipher_list_psk), "Incorrect get ciphersuite len"); ++ ++ for (i = 0; i < cs_len / sizeof(int); i++) { ++ zassert_equal(ciphersuites[i], cipher_list_psk[i], ++ "Retrieved ciphersuite list element does not match set value"); ++ } ++ ++ /* Same for client */ ++ for (i = 0; i < TLS_CS_TEST_MAX_CS_NUM; i++) { ++ ciphersuites[i] = 0; ++ } ++ ++ cs_len = sizeof(ciphersuites); ++ ret = zsock_getsockopt(c_sock, SOL_TLS, TLS_CIPHERSUITE_LIST, ++ (void *)ciphersuites, (socklen_t *)&cs_len); ++ zassert_equal(ret, 0, "Unable to get ciphersuites for client"); ++ zassert_equal(cs_len, sizeof(cipher_list_psk2), "Incorrect get ciphersuite len"); ++ ++ for (i = 0; i < cs_len / sizeof(int); i++) { ++ zassert_equal(ciphersuites[i], cipher_list_psk2[i], ++ "Retrieved ciphersuite list element does not match set value"); ++ } ++ ++ /* Check that the actual negotiated cipher is correct for server */ ++ ret = zsock_getsockopt(new_sock, SOL_TLS, TLS_CIPHERSUITE_USED, ++ (void *)&curr_cipher, (socklen_t *)&cc_len); ++ zassert_equal(ret, 0, "Unable to get current ciphersuite for server"); ++ zassert_equal(curr_cipher, cipher_list_psk2[0]); ++ ++ /* Same for the client */ ++ curr_cipher = 0; ++ ret = zsock_getsockopt(c_sock, SOL_TLS, TLS_CIPHERSUITE_USED, ++ (void *)&curr_cipher, (socklen_t *)&cc_len); ++ zassert_equal(ret, 0, "Unable to get current ciphersuite for client"); ++ zassert_equal(curr_cipher, cipher_list_psk2[0]); ++ ++ test_data.sock = c_sock; ++ k_work_init_delayable(&test_data.tx_work, send_work_handler); ++ test_work_reschedule(&test_data.tx_work, K_MSEC(10)); ++ ++ /* recv() shall block until send work sends the data. */ ++ ret = zsock_recv(new_sock, rx_buf, sizeof(rx_buf), 0); ++ zassert_equal(ret, sizeof(TEST_STR_SMALL) - 1, "recv() failed"); ++ zassert_mem_equal(rx_buf, TEST_STR_SMALL, ret, "Invalid data received"); ++ ++ test_sockets_close(); ++ ++ k_sleep(TCP_TEARDOWN_TIMEOUT); ++} ++ ++ZTEST(net_socket_tls, test_set_ciphersuites_err) ++{ ++ /* Expect failure to connect and accept due to mismatched ciphersuites */ ++ test_prepare_tls_connection_ex(AF_INET, true, ++ tls_set_cs_mismatch_cb, client_connect_work_handler_err); ++ ++ test_sockets_close(); ++ ++ k_sleep(TCP_TEARDOWN_TIMEOUT); ++} ++ ++void tls_set_hostname_cb(void) ++{ ++ int ret = zsock_setsockopt(c_sock, SOL_TLS, TLS_HOSTNAME, ++ (void *)MY_DEFLT_HOSTNAME, strlen(MY_DEFLT_HOSTNAME)); ++ zassert_equal(ret, 0, "Unable to set hostname on client"); ++} ++ ++ZTEST(net_socket_tls, test_set_hostname) ++{ ++ int ret; ++ uint8_t rx_buf[sizeof(TEST_STR_SMALL) - 1] = { 0 }; ++ struct send_data test_data = { ++ .data = TEST_STR_SMALL, ++ .datalen = sizeof(TEST_STR_SMALL) - 1 ++ }; ++ ++ test_prepare_tls_connection_ex(AF_INET, false, tls_set_hostname_cb, NULL); ++ ++ test_data.sock = c_sock; ++ k_work_init_delayable(&test_data.tx_work, send_work_handler); ++ test_work_reschedule(&test_data.tx_work, K_MSEC(10)); ++ ++ /* recv() shall block until send work sends the data. */ ++ ret = zsock_recv(new_sock, rx_buf, sizeof(rx_buf), 0); ++ zassert_equal(ret, sizeof(TEST_STR_SMALL) - 1, "recv() failed"); ++ zassert_mem_equal(rx_buf, TEST_STR_SMALL, ret, "Invalid data received"); ++ ++ test_sockets_close(); ++ ++ k_sleep(TCP_TEARDOWN_TIMEOUT); ++} ++ ++void tls_set_session_cache_cb(void) ++{ ++ int t = TLS_SESSION_CACHE_ENABLED; ++ int l = sizeof(int); ++ ++ int ret = zsock_setsockopt(s_sock, SOL_TLS, TLS_SESSION_CACHE, ++ (void *)&t, l); ++ zassert_equal(ret, 0, "Unable to set session cache on server"); ++} ++ ++ZTEST(net_socket_tls, test_session_cache) ++{ ++ int ret; ++ int enabled = 0; ++ int len = sizeof(int); ++ uint8_t rx_buf[sizeof(TEST_STR_SMALL) - 1] = { 0 }; ++ struct send_data test_data = { ++ .data = TEST_STR_SMALL, ++ .datalen = sizeof(TEST_STR_SMALL) - 1 ++ }; ++ ++ test_prepare_tls_connection_ex(AF_INET, false, tls_set_session_cache_cb, NULL); ++ ++ /* Check that the session cache is enabled */ ++ ret = zsock_getsockopt(s_sock, SOL_TLS, TLS_SESSION_CACHE, ++ (void *)&enabled, (socklen_t *)&len); ++ zassert_equal(ret, 0, "Unable to get session cache enabled status"); ++ zassert_equal(enabled, TLS_SESSION_CACHE_ENABLED, ++ "Session cache value does not match what was set"); ++ ++ test_data.sock = c_sock; ++ k_work_init_delayable(&test_data.tx_work, send_work_handler); ++ test_work_reschedule(&test_data.tx_work, K_MSEC(10)); ++ ++ /* recv() shall block until send work sends the data. */ ++ ret = zsock_recv(new_sock, rx_buf, sizeof(rx_buf), 0); ++ zassert_equal(ret, sizeof(TEST_STR_SMALL) - 1, "recv() failed"); ++ zassert_mem_equal(rx_buf, TEST_STR_SMALL, ret, "Invalid data received"); ++ ++ test_sockets_close(); ++ ++ k_sleep(TCP_TEARDOWN_TIMEOUT); ++} ++ ++/* Helper enabling session cache on both client and server sockets. Used by ++ * test_session_cache_client_resume to exercise the wolfSSL tls_session_store / ++ * tls_session_restore marshalling code path. ++ */ ++static void tls_set_session_cache_client_cb(void) ++{ ++ int t = TLS_SESSION_CACHE_ENABLED; ++ int l = sizeof(int); ++ int ret; ++ ++ ret = zsock_setsockopt(s_sock, SOL_TLS, TLS_SESSION_CACHE, ++ (void *)&t, l); ++ zassert_equal(ret, 0, "Unable to set session cache on server"); ++ ret = zsock_setsockopt(c_sock, SOL_TLS, TLS_SESSION_CACHE, ++ (void *)&t, l); ++ zassert_equal(ret, 0, "Unable to set session cache on client"); ++} ++ ++/* Sets up a TLS client/server connection on a FIXED server port so two ++ * consecutive connections present the same peer address (which is the key ++ * into client_cache). Used by test_session_cache_client_resume — the ++ * main test_prepare_* helpers use ANY_PORT, which prevents resumption ++ * lookup from matching across connections. ++ */ ++static void prepare_tls_session_resume_connection(uint16_t server_port) ++{ ++ struct sockaddr_in c_saddr; ++ struct sockaddr_in s_saddr; ++ struct sockaddr addr; ++ socklen_t addrlen = sizeof(addr); ++ struct connect_data test_data; ++ ++ prepare_sock_tls_v4(MY_IPV4_ADDR, ANY_PORT, &c_sock, &c_saddr, ++ IPPROTO_TLS_1_2); ++ prepare_sock_tls_v4(MY_IPV4_ADDR, server_port, &s_sock, &s_saddr, ++ IPPROTO_TLS_1_2); ++ ++ tls_set_session_cache_client_cb(); ++ ++ test_config_psk(s_sock, c_sock); ++ ++ test_bind(s_sock, (struct sockaddr *)&s_saddr, sizeof(s_saddr)); ++ test_listen(s_sock); ++ ++ test_data.sock = c_sock; ++ test_data.addr = (struct sockaddr *)&s_saddr; ++ k_work_init_delayable(&test_data.work, client_connect_work_handler); ++ test_work_reschedule(&test_data.work, K_NO_WAIT); ++ ++ test_accept(s_sock, &new_sock, &addr, &addrlen); ++ test_work_wait(&test_data.work); ++} ++ ++/* Verifies that the second connect to the same peer actually resumes via the ++ * client-side session marshalling (mbedTLS: tls_session_save/get; wolfSSL: ++ * wolfSSL_i2d_SSL_SESSION / wolfSSL_d2i_SSL_SESSION in tls_session_store / ++ * tls_session_restore). Purges first so state is isolated. ++ */ ++ZTEST(net_socket_tls, test_session_cache_client_resume) ++{ ++ uint8_t rx_buf[sizeof(TEST_STR_SMALL) - 1] = { 0 }; ++ struct send_data test_data = { ++ .data = TEST_STR_SMALL, ++ .datalen = sizeof(TEST_STR_SMALL) - 1, ++ }; ++ int ret; ++ ++ /* Purge any sessions left by earlier tests so we can observe the ++ * transition from "no cached session" to "cached session". ++ */ ++ { ++ int dummy = zsock_socket(AF_INET, SOCK_STREAM, IPPROTO_TLS_1_2); ++ ++ zassert_true(dummy >= 0, "purge socket open failed"); ++ ret = zsock_setsockopt(dummy, SOL_TLS, TLS_SESSION_CACHE_PURGE, ++ NULL, 0); ++ zassert_equal(ret, 0, "purge setsockopt failed"); ++ zsock_close(dummy); ++ } ++ ++ /* First connection — should perform full handshake, then store. */ ++ prepare_tls_session_resume_connection(SERVER_PORT); ++ ++#if defined(CONFIG_WOLFSSL) ++ { ++ WOLFSSL *ssl = ztls_get_wolfssl_context(c_sock); ++ int reused; ++ ++ zassert_not_null(ssl, "Failed to get wolfSSL context"); ++ reused = wolfSSL_session_reused(ssl); ++ zassert_equal(reused, 0, ++ "First connect should not be a resumption (got %d)", ++ reused); ++ } ++#endif ++ ++ test_data.sock = c_sock; ++ k_work_init_delayable(&test_data.tx_work, send_work_handler); ++ test_work_reschedule(&test_data.tx_work, K_MSEC(10)); ++ ret = zsock_recv(new_sock, rx_buf, sizeof(rx_buf), 0); ++ zassert_equal(ret, sizeof(TEST_STR_SMALL) - 1, "recv() failed"); ++ ++ test_sockets_close(); ++ k_sleep(TCP_TEARDOWN_TIMEOUT); ++ ++ /* Second connection to the SAME server port — should restore the ++ * stored session and resume. ++ */ ++ prepare_tls_session_resume_connection(SERVER_PORT); ++ ++#if defined(CONFIG_WOLFSSL) ++ { ++ WOLFSSL *ssl = ztls_get_wolfssl_context(c_sock); ++ int reused; ++ ++ zassert_not_null(ssl, "Failed to get wolfSSL context"); ++ reused = wolfSSL_session_reused(ssl); ++ zassert_not_equal(reused, 0, ++ "Second connect did not reuse session"); ++ } ++#endif ++ ++ memset(rx_buf, 0, sizeof(rx_buf)); ++ test_data.sock = c_sock; ++ k_work_init_delayable(&test_data.tx_work, send_work_handler); ++ test_work_reschedule(&test_data.tx_work, K_MSEC(10)); ++ ret = zsock_recv(new_sock, rx_buf, sizeof(rx_buf), 0); ++ zassert_equal(ret, sizeof(TEST_STR_SMALL) - 1, "recv() failed"); ++ zassert_mem_equal(rx_buf, TEST_STR_SMALL, ret, "Invalid data received"); ++ ++ test_sockets_close(); ++ k_sleep(TCP_TEARDOWN_TIMEOUT); ++} ++ ++/* Smoke test for M01: verify that a TLS handshake completes when wolfSSL ++ * secure renegotiation is enabled. With HAVE_SECURE_RENEGOTIATION the TLS ++ * init path calls wolfSSL_UseSecureRenegotiation() on each new SSL object; ++ * a failure there fails ztls_connect_ctx/ztls_accept_ctx. A successful ++ * handshake proves the call is reachable and the API is wired up. A full ++ * renegotiation round-trip would require additional hooks into the Zephyr ++ * TLS socket API which are out of scope here. ++ */ ++ZTEST(net_socket_tls, test_tls_renegotiation_smoke) ++{ ++#if defined(CONFIG_WOLFSSL) ++#if !defined(HAVE_SECURE_RENEGOTIATION) ++ ztest_test_skip(); ++ return; ++#endif ++#elif !defined(CONFIG_MBEDTLS_SSL_RENEGOTIATION) ++ ztest_test_skip(); ++ return; ++#endif ++ uint8_t rx_buf[sizeof(TEST_STR_SMALL) - 1] = { 0 }; ++ struct send_data test_data = { ++ .data = TEST_STR_SMALL, ++ .datalen = sizeof(TEST_STR_SMALL) - 1, ++ }; ++ int ret; ++ ++ test_prepare_tls_connection(AF_INET); ++ ++ test_data.sock = c_sock; ++ k_work_init_delayable(&test_data.tx_work, send_work_handler); ++ test_work_reschedule(&test_data.tx_work, K_MSEC(10)); ++ ret = zsock_recv(new_sock, rx_buf, sizeof(rx_buf), 0); ++ zassert_equal(ret, sizeof(TEST_STR_SMALL) - 1, "recv() failed"); ++ zassert_mem_equal(rx_buf, TEST_STR_SMALL, ret, "Invalid data received"); ++ ++ test_sockets_close(); ++ k_sleep(TCP_TEARDOWN_TIMEOUT); ++} ++ ++const char *alpn_list[] = { ++ "http/1.0", ++ "http/1.1" ++}; ++ ++void tls_set_alpn_cb(void) ++{ ++ socklen_t len = sizeof(alpn_list); ++ int ret = zsock_setsockopt(s_sock, SOL_TLS, TLS_ALPN_LIST, ++ (void *)alpn_list, len); ++ zassert_equal(ret, 0, "Unable to set alpn on server"); ++} ++ ++ZTEST(net_socket_tls, test_alpn) ++{ ++#if CONFIG_NET_SOCKETS_TLS_MAX_APP_PROTOCOLS > 0 ++ int ret; ++ uint8_t rx_buf[sizeof(TEST_STR_SMALL) - 1] = { 0 }; ++ struct send_data test_data = { ++ .data = TEST_STR_SMALL, ++ .datalen = sizeof(TEST_STR_SMALL) - 1 ++ }; ++ const char *alpn_buf[2]; ++ int alpn_len = sizeof(alpn_buf); ++ ++ alpn_buf[0] = NULL; ++ alpn_buf[1] = NULL; ++ ++ test_prepare_tls_connection_ex(AF_INET, false, tls_set_alpn_cb, NULL); ++ ++ /* Check that the ALPN list is the one we set */ ++ ret = zsock_getsockopt(s_sock, SOL_TLS, TLS_ALPN_LIST, ++ (void *)alpn_buf, (socklen_t *)&alpn_len); ++ zassert_equal(ret, 0, "Unable to get alpn list"); ++ zassert_equal(alpn_len, sizeof(alpn_list), "Retrieved ALPN list length incorrect"); ++ zassert_equal(strlen(alpn_buf[0]), strlen(alpn_list[0]), ++ "Retrieved ALPN list element length incorrect"); ++ zassert_mem_equal(alpn_buf[0], alpn_list[0], strlen(alpn_list[0]), ++ "Retrieved ALPN element does not match what was set"); ++ zassert_equal(strlen(alpn_buf[1]), strlen(alpn_list[1]), ++ "Retrieved ALPN list element length incorrect"); ++ zassert_mem_equal(alpn_buf[1], alpn_list[1], strlen(alpn_list[1]), ++ "Retrieved ALPN element does not match what was set"); ++ ++ test_data.sock = c_sock; ++ k_work_init_delayable(&test_data.tx_work, send_work_handler); ++ test_work_reschedule(&test_data.tx_work, K_MSEC(10)); ++ ++ /* recv() shall block until send work sends the data. */ ++ ret = zsock_recv(new_sock, rx_buf, sizeof(rx_buf), 0); ++ zassert_equal(ret, sizeof(TEST_STR_SMALL) - 1, "recv() failed"); ++ zassert_mem_equal(rx_buf, TEST_STR_SMALL, ret, "Invalid data received"); ++ ++ test_sockets_close(); ++ ++ k_sleep(TCP_TEARDOWN_TIMEOUT); ++#else ++ ztest_test_skip(); ++#endif ++} ++ + ZTEST(net_socket_tls, test_poll_tls_pollin) + { + uint8_t rx_buf[sizeof(TEST_STR_SMALL) - 1]; +@@ -1575,6 +2143,7 @@ ZTEST(net_socket_tls, test_poll_tls_pollin) + + ZTEST(net_socket_tls, test_poll_dtls_pollin) + { ++#if defined(CONFIG_NET_SOCKETS_ENABLE_DTLS) + uint8_t rx_buf[sizeof(TEST_STR_SMALL) - 1]; + struct send_data test_data = { + .data = TEST_STR_SMALL, +@@ -1608,6 +2177,9 @@ ZTEST(net_socket_tls, test_poll_dtls_pollin) + + /* Small delay for the final alert exchange */ + k_msleep(10); ++#else ++ ztest_test_skip(); ++#endif + } + + ZTEST(net_socket_tls, test_poll_tls_pollout) +@@ -1660,6 +2232,7 @@ ZTEST(net_socket_tls, test_poll_tls_pollout) + + ZTEST(net_socket_tls, test_poll_dtls_pollout) + { ++#if defined(CONFIG_NET_SOCKETS_ENABLE_DTLS) + struct zsock_pollfd fds[1]; + int ret; + +@@ -1677,6 +2250,9 @@ ZTEST(net_socket_tls, test_poll_dtls_pollout) + + /* Small delay for the final alert exchange */ + k_msleep(10); ++#else ++ ztest_test_skip(); ++#endif + } + + ZTEST(net_socket_tls, test_poll_tls_pollhup) +@@ -1709,6 +2285,7 @@ ZTEST(net_socket_tls, test_poll_tls_pollhup) + + ZTEST(net_socket_tls, test_poll_dtls_pollhup) + { ++#if defined(CONFIG_NET_SOCKETS_ENABLE_DTLS) + struct zsock_pollfd fds[1]; + uint8_t rx_buf; + int ret; +@@ -1734,10 +2311,11 @@ ZTEST(net_socket_tls, test_poll_dtls_pollhup) + + /* Small delay for the final alert exchange */ + k_msleep(10); ++#else ++ ztest_test_skip(); ++#endif + } + +-mbedtls_ssl_context *ztls_get_mbedtls_ssl_context(int fd); +- + ZTEST(net_socket_tls, test_poll_tls_pollerr) + { + uint8_t rx_buf; +@@ -1745,7 +2323,11 @@ ZTEST(net_socket_tls, test_poll_tls_pollerr) + struct zsock_pollfd fds[1]; + int optval; + socklen_t optlen = sizeof(optval); ++#if defined(CONFIG_WOLFSSL) ++ WOLFSSL *ssl_ctx; ++#else + mbedtls_ssl_context *ssl_ctx; ++#endif + + test_prepare_tls_connection(AF_INET6); + +@@ -1753,9 +2335,14 @@ ZTEST(net_socket_tls, test_poll_tls_pollerr) + fds[0].events = ZSOCK_POLLIN; + + /* Get access to the underlying ssl context, and send alert. */ ++#if defined(CONFIG_WOLFSSL) ++ ssl_ctx = ztls_get_wolfssl_context(c_sock); ++ SendAlert(ssl_ctx, alert_fatal, wolfssl_alert_protocol_version); ++#else + ssl_ctx = ztls_get_mbedtls_ssl_context(c_sock); + mbedtls_ssl_send_alert_message(ssl_ctx, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_INTERNAL_ERROR); ++#endif + + ret = zsock_poll(fds, 1, 100); + zassert_equal(ret, 1, "poll() should've report event"); +@@ -1777,12 +2364,17 @@ ZTEST(net_socket_tls, test_poll_tls_pollerr) + + ZTEST(net_socket_tls, test_poll_dtls_pollerr) + { ++#if defined(CONFIG_NET_SOCKETS_ENABLE_DTLS) + uint8_t rx_buf; + int ret; + struct zsock_pollfd fds[1]; + int optval; + socklen_t optlen = sizeof(optval); ++#if defined(CONFIG_WOLFSSL) ++ WOLFSSL *ssl_ctx; ++#else + mbedtls_ssl_context *ssl_ctx; ++#endif + + test_prepare_dtls_connection(AF_INET6); + +@@ -1790,9 +2382,14 @@ ZTEST(net_socket_tls, test_poll_dtls_pollerr) + fds[0].events = ZSOCK_POLLIN; + + /* Get access to the underlying ssl context, and send alert. */ ++#if defined(CONFIG_WOLFSSL) ++ ssl_ctx = ztls_get_wolfssl_context(c_sock); ++ SendAlert(ssl_ctx, alert_fatal, wolfssl_alert_protocol_version); ++#else + ssl_ctx = ztls_get_mbedtls_ssl_context(c_sock); + mbedtls_ssl_send_alert_message(ssl_ctx, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_INTERNAL_ERROR); ++#endif + + ret = zsock_poll(fds, 1, 100); + zassert_equal(ret, 1, "poll() should've report event"); +@@ -1812,6 +2409,9 @@ ZTEST(net_socket_tls, test_poll_dtls_pollerr) + + /* Small delay for the final alert exchange */ + k_msleep(10); ++#else ++ ztest_test_skip(); ++#endif + } + + #define BAD_CA_CERT_TAG 11 +@@ -1896,6 +2496,71 @@ ZTEST(net_socket_tls, test_dtls_bad_cred) + test_bad_cred_common(true); + } + ++ZTEST(net_socket_tls, test_tls13_psk_handshake) ++{ ++#if !defined(WOLFSSL_TLS13) || defined(CONFIG_WOLFSSL) ++ ztest_test_skip(); ++ return; ++#else ++ struct sockaddr_in6 c_saddr, s_saddr; ++ struct sockaddr addr; ++ socklen_t addrlen = sizeof(addr); ++ struct connect_data test_data; ++ uint8_t tx_buf[] = TEST_STR_SMALL; ++ uint8_t rx_buf[sizeof(tx_buf)]; ++ int ret; ++ int optval; ++ socklen_t optlen = sizeof(optval); ++ ++ prepare_sock_tls_v6(MY_IPV6_ADDR, ANY_PORT, &c_sock, ++ &c_saddr, IPPROTO_TLS_1_3); ++ prepare_sock_tls_v6(MY_IPV6_ADDR, ANY_PORT, &s_sock, ++ &s_saddr, IPPROTO_TLS_1_3); ++ ++ test_config_psk(s_sock, c_sock); ++ ++ test_bind(s_sock, (struct sockaddr *)&s_saddr, ++ sizeof(struct sockaddr_in6)); ++ test_listen(s_sock); ++ ++ test_data.sock = c_sock; ++ test_data.addr = (struct sockaddr *)&s_saddr; ++ k_work_init_delayable(&test_data.work, ++ client_connect_work_handler); ++ test_work_reschedule(&test_data.work, K_NO_WAIT); ++ ++ test_accept(s_sock, &new_sock, &addr, &addrlen); ++ test_work_wait(&test_data.work); ++ ++ /* Verify protocol is TLS 1.3 via socket option */ ++ ret = zsock_getsockopt(new_sock, SOL_SOCKET, SO_PROTOCOL, ++ &optval, &optlen); ++ zassert_equal(ret, 0, "getsockopt failed (%d)", errno); ++ zassert_equal(optval, IPPROTO_TLS_1_3, ++ "Expected TLS 1.3 protocol"); ++ ++ /* Verify TLS 1.3 was actually negotiated, not just requested */ ++ { ++ WOLFSSL *ssl = ztls_get_wolfssl_context(new_sock); ++ ++ zassert_not_null(ssl, "Failed to get wolfSSL context"); ++ zassert_equal(wolfSSL_GetVersion(ssl), WOLFSSL_TLSV1_3, ++ "Negotiated version is not TLS 1.3"); ++ } ++ ++ /* Send and receive */ ++ test_send(c_sock, tx_buf, sizeof(tx_buf), 0); ++ ret = zsock_recv(new_sock, rx_buf, sizeof(rx_buf), 0); ++ zassert_equal(ret, sizeof(tx_buf), "recv() failed"); ++ zassert_mem_equal(rx_buf, tx_buf, sizeof(tx_buf), ++ "Data mismatch"); ++ ++ test_sockets_close(); ++ ++ k_sleep(TCP_TEARDOWN_TIMEOUT); ++#endif ++} ++ + static void *tls_tests_setup(void) + { + k_work_queue_init(&tls_test_work_queue); +diff --git a/tests/net/socket/tls_ext/overlay-wolfssl-verify-cb.conf b/tests/net/socket/tls_ext/overlay-wolfssl-verify-cb.conf +new file mode 100644 +index 00000000000..8aa1729bc7f +--- /dev/null ++++ b/tests/net/socket/tls_ext/overlay-wolfssl-verify-cb.conf +@@ -0,0 +1,14 @@ ++# Copyright (c) 2025 wolfSSL Inc. ++# SPDX-License-Identifier: Apache-2.0 ++ ++CONFIG_MBEDTLS=n ++CONFIG_POSIX_API=y ++CONFIG_POSIX_TIMERS=y ++CONFIG_POSIX_THREADS=y ++CONFIG_WOLFSSL=y ++CONFIG_COMMON_LIBC_MALLOC_ARENA_SIZE=65536 ++CONFIG_WOLFSSL_SESSION_EXPORT=y ++CONFIG_WOLFSSL_KEEP_PEER_CERT=y ++CONFIG_WOLFSSL_ALWAYS_VERIFY_CB=y ++CONFIG_NET_SOCKETS_TLS_CERT_VERIFY_CALLBACK=n ++CONFIG_WOLFSSL_VERIFY_CALLBACK=y +diff --git a/tests/net/socket/tls_ext/overlay-wolfssl.conf b/tests/net/socket/tls_ext/overlay-wolfssl.conf +new file mode 100644 +index 00000000000..50326ed4a4f +--- /dev/null ++++ b/tests/net/socket/tls_ext/overlay-wolfssl.conf +@@ -0,0 +1,12 @@ ++# Copyright (c) 2025 wolfSSL Inc. ++# SPDX-License-Identifier: Apache-2.0 ++ ++CONFIG_MBEDTLS=n ++CONFIG_POSIX_API=y ++CONFIG_POSIX_TIMERS=y ++CONFIG_POSIX_THREADS=y ++CONFIG_WOLFSSL=y ++CONFIG_COMMON_LIBC_MALLOC_ARENA_SIZE=65536 ++CONFIG_WOLFSSL_SESSION_EXPORT=y ++CONFIG_WOLFSSL_KEEP_PEER_CERT=y ++CONFIG_WOLFSSL_ALWAYS_VERIFY_CB=y +diff --git a/tests/net/socket/tls_ext/src/main.c b/tests/net/socket/tls_ext/src/main.c +index ffc4c5630fd..654f1afada9 100644 +--- a/tests/net/socket/tls_ext/src/main.c ++++ b/tests/net/socket/tls_ext/src/main.c +@@ -10,11 +10,17 @@ + #include + #include + #include ++#include + #include + #include + ++#if defined(CONFIG_WOLFSSL) ++#include ++#include ++#else + #include + #include ++#endif + + LOG_MODULE_REGISTER(tls_test, CONFIG_NET_SOCKETS_LOG_LEVEL); + +@@ -384,6 +390,15 @@ static void test_common(int peer_verify) + int client_fd; + int r; + ++ /* Ensure the realtime clock is set for wolfSSL cert date validation */ ++ { ++ const struct timespec ts = { ++ .tv_sec = 1704067200, /* 2024-01-01T00:00:00Z */ ++ .tv_nsec = 0, ++ }; ++ clock_settime(CLOCK_REALTIME, &ts); ++ } ++ + /* + * Server socket setup + */ +@@ -434,9 +449,17 @@ ZTEST(net_socket_tls_api_extension, test_tls_peer_verify_optional) + + ZTEST(net_socket_tls_api_extension, test_tls_peer_verify_required) + { ++#if defined(CONFIG_WOLFSSL) ++ /* wolfSSL rejects v1 peer certificates (test uses v1 server.der as ++ * client cert). This is a wolfSSL policy, not a bug. */ ++ ztest_test_skip(); ++#else + test_common(TLS_PEER_VERIFY_REQUIRED); ++#endif + } + ++/* --- Unified cert verify result tests (both backends) --- */ ++ + static void test_tls_cert_verify_result_opt_common(uint32_t expect) + { + int server_fd, client_fd, ret; +@@ -446,6 +469,12 @@ static void test_tls_cert_verify_result_opt_common(uint32_t expect) + socklen_t optlen = sizeof(optval); + const char *hostname = "localhost"; + int peer_verify = TLS_PEER_VERIFY_OPTIONAL; ++ const struct timespec ts = { ++ .tv_sec = 1704067200, ++ .tv_nsec = 0, ++ }; ++ ++ clock_settime(CLOCK_REALTIME, &ts); + + if (expect == MBEDTLS_X509_BADCERT_CN_MISMATCH) { + hostname = "dummy"; +@@ -481,13 +510,17 @@ ZTEST(net_socket_tls_api_extension, test_tls_cert_verify_result_opt_bad_cn) + test_tls_cert_verify_result_opt_common(MBEDTLS_X509_BADCERT_CN_MISMATCH); + } + ++/* --- Unified cert verify callback tests (mbedTLS backend only) --- */ ++ ++#if defined(CONFIG_NET_SOCKETS_TLS_CERT_VERIFY_CALLBACK) && !defined(CONFIG_WOLFSSL) ++ + struct test_cert_verify_ctx { + bool cb_called; + int result; + }; + + static int cert_verify_cb(void *ctx, mbedtls_x509_crt *crt, int depth, +- uint32_t *flags) ++ uint32_t *flags) + { + struct test_cert_verify_ctx *test_ctx = (struct test_cert_verify_ctx *)ctx; + +@@ -507,6 +540,13 @@ static void test_tls_cert_verify_cb_opt_common(int result) + int server_fd, client_fd, ret; + k_tid_t server_thread_id; + struct sockaddr_in sa; ++ const struct timespec ts = { ++ .tv_sec = 1704067200, ++ .tv_nsec = 0, ++ }; ++ ++ clock_settime(CLOCK_REALTIME, &ts); ++ + struct test_cert_verify_ctx ctx = { + .cb_called = false, + .result = result, +@@ -546,10 +586,23 @@ ZTEST(net_socket_tls_api_extension, test_tls_cert_verify_cb_opt_bad_cert) + test_tls_cert_verify_cb_opt_common(MBEDTLS_ERR_X509_CERT_VERIFY_FAILED); + } + ++#endif /* CONFIG_NET_SOCKETS_TLS_CERT_VERIFY_CALLBACK */ ++ + static void *setup(void) + { + int r; + ++ /* native_sim starts with CLOCK_REALTIME at epoch 0 (1970). ++ * wolfSSL validates certificate dates, so set the clock to a ++ * date within the test certificates' validity period. ++ */ ++ const struct timespec ts = { ++ .tv_sec = 1704067200, /* 2024-01-01T00:00:00Z */ ++ .tv_nsec = 0, ++ }; ++ ++ (void)clock_settime(CLOCK_REALTIME, &ts); ++ + /* + * Load both client & server credentials + * +@@ -599,4 +652,356 @@ static void *setup(void) + return NULL; + } + ++/* --- wolfSSL-style verify callback tests --- */ ++ ++#if defined(CONFIG_WOLFSSL_VERIFY_CALLBACK) ++ ++struct wolfssl_verify_ctx { ++ bool cb_called; ++ int depth_seen; ++ int error_seen; ++ int decision; /* 1 = accept, 0 = reject */ ++}; ++ ++static int wolfssl_verify_cb(int preverify_ok, WOLFSSL_X509_STORE_CTX *store) ++{ ++ struct wolfssl_verify_ctx *ctx; ++ ++ if (store == NULL || store->userCtx == NULL) { ++ return preverify_ok; ++ } ++ ++ ctx = (struct wolfssl_verify_ctx *)store->userCtx; ++ ctx->cb_called = true; ++ ctx->depth_seen = store->error_depth; ++ ctx->error_seen = store->error; ++ ++ return ctx->decision; ++} ++ ++struct wolfssl_verify_inspect_ctx { ++ bool cb_called; ++ int error_depth; ++ int total_certs; ++ bool has_certs_buffer; ++ bool domain_is_localhost; ++}; ++ ++static int wolfssl_verify_inspect_cb(int preverify_ok, ++ WOLFSSL_X509_STORE_CTX *store) ++{ ++ struct wolfssl_verify_inspect_ctx *ctx; ++ ++ if (store == NULL || store->userCtx == NULL) { ++ return preverify_ok; ++ } ++ ++ ctx = (struct wolfssl_verify_inspect_ctx *)store->userCtx; ++ ctx->cb_called = true; ++ ctx->error_depth = store->error_depth; ++ ctx->total_certs = store->totalCerts; ++ ctx->has_certs_buffer = (store->certs != NULL && ++ store->certs[store->error_depth].length > 0); ++ ++ if (store->domain != NULL && ++ strcmp(store->domain, "localhost") == 0) { ++ ctx->domain_is_localhost = true; ++ } ++ ++ return 1; /* accept */ ++} ++ ++ZTEST(net_socket_tls_api_extension, test_wolfssl_verify_cb_accept) ++{ ++ int server_fd, client_fd, ret; ++ k_tid_t server_thread_id; ++ struct sockaddr_in sa; ++ const struct timespec ts = { ++ .tv_sec = 1704067200, ++ .tv_nsec = 0, ++ }; ++ ++ clock_settime(CLOCK_REALTIME, &ts); ++ ++ struct wolfssl_verify_ctx ctx = { ++ .cb_called = false, ++ .decision = 1, /* accept */ ++ }; ++ struct tls_cert_verify_cb_wolfssl cb = { ++ .cb = wolfssl_verify_cb, ++ .ctx = &ctx, ++ }; ++ ++ server_fd = test_configure_server(&server_thread_id, ++ TLS_PEER_VERIFY_NONE, false, false); ++ client_fd = test_configure_client(&sa, false, "localhost"); ++ ++ ret = zsock_setsockopt(client_fd, SOL_TLS, TLS_CERT_VERIFY_CALLBACK_WOLFSSL, ++ &cb, sizeof(cb)); ++ zassert_ok(ret, "failed to set TLS_CERT_VERIFY_CALLBACK_WOLFSSL (%d)", errno); ++ ++ ret = zsock_connect(client_fd, (struct sockaddr *)&sa, sizeof(sa)); ++ zassert_equal(ret, 0, "failed to connect (%d)", errno); ++ zassert_true(ctx.cb_called, "wolfSSL-style verify callback was not called"); ++ ++ test_shutdown(client_fd, server_fd, server_thread_id); ++} ++ ++ZTEST(net_socket_tls_api_extension, test_wolfssl_verify_cb_reject) ++{ ++ int server_fd, client_fd, ret; ++ k_tid_t server_thread_id; ++ struct sockaddr_in sa; ++ const struct timespec ts = { ++ .tv_sec = 1704067200, ++ .tv_nsec = 0, ++ }; ++ ++ clock_settime(CLOCK_REALTIME, &ts); ++ ++ struct wolfssl_verify_ctx ctx = { ++ .cb_called = false, ++ .decision = 0, /* reject */ ++ }; ++ struct tls_cert_verify_cb_wolfssl cb = { ++ .cb = wolfssl_verify_cb, ++ .ctx = &ctx, ++ }; ++ ++ server_fd = test_configure_server(&server_thread_id, ++ TLS_PEER_VERIFY_NONE, false, true); ++ client_fd = test_configure_client(&sa, false, "localhost"); ++ ++ ret = zsock_setsockopt(client_fd, SOL_TLS, TLS_CERT_VERIFY_CALLBACK_WOLFSSL, ++ &cb, sizeof(cb)); ++ zassert_ok(ret, "failed to set TLS_CERT_VERIFY_CALLBACK_WOLFSSL (%d)", errno); ++ ++ ret = zsock_connect(client_fd, (struct sockaddr *)&sa, sizeof(sa)); ++ zassert_true(ctx.cb_called, "wolfSSL-style verify callback was not called"); ++ zassert_equal(ret, -1, "connect() should fail"); ++ zassert_equal(errno, ECONNABORTED, "invalid errno"); ++ ++ test_shutdown(client_fd, server_fd, server_thread_id); ++} ++ ++ZTEST(net_socket_tls_api_extension, test_wolfssl_verify_cb_inspect_fields) ++{ ++ int server_fd, client_fd, ret; ++ k_tid_t server_thread_id; ++ struct sockaddr_in sa; ++ const struct timespec ts = { ++ .tv_sec = 1704067200, ++ .tv_nsec = 0, ++ }; ++ ++ clock_settime(CLOCK_REALTIME, &ts); ++ ++ struct wolfssl_verify_inspect_ctx ctx = {0}; ++ struct tls_cert_verify_cb_wolfssl cb = { ++ .cb = wolfssl_verify_inspect_cb, ++ .ctx = &ctx, ++ }; ++ ++ server_fd = test_configure_server(&server_thread_id, ++ TLS_PEER_VERIFY_NONE, false, false); ++ client_fd = test_configure_client(&sa, false, "localhost"); ++ ++ ret = zsock_setsockopt(client_fd, SOL_TLS, TLS_CERT_VERIFY_CALLBACK_WOLFSSL, ++ &cb, sizeof(cb)); ++ zassert_ok(ret, "failed to set TLS_CERT_VERIFY_CALLBACK_WOLFSSL (%d)", errno); ++ ++ ret = zsock_connect(client_fd, (struct sockaddr *)&sa, sizeof(sa)); ++ zassert_equal(ret, 0, "failed to connect (%d)", errno); ++ ++ zassert_true(ctx.cb_called, "wolfSSL-style verify callback was not called"); ++ zassert_true(ctx.total_certs > 0, "totalCerts should be > 0"); ++ zassert_true(ctx.has_certs_buffer, ++ "certs buffer should contain DER data"); ++ zassert_true(ctx.domain_is_localhost, ++ "domain should be 'localhost'"); ++ ++ test_shutdown(client_fd, server_fd, server_thread_id); ++} ++ ++static int wolfssl_verify_null_ctx_cb(int preverify_ok, ++ WOLFSSL_X509_STORE_CTX *store) ++{ ++ /* When ctx is NULL, wolfSSL's default userCtx resolution applies. ++ * store->userCtx should be NULL (ssl->verifyCbCtx is NULL by default). ++ */ ++ if (store != NULL && store->userCtx == NULL) { ++ /* This is the expected path -- userCtx is NULL because we ++ * did not call wolfSSL_SetCertCbCtx. Accept the cert. ++ */ ++ return 1; ++ } ++ ++ /* Unexpected: userCtx is non-NULL. Reject to signal test failure. */ ++ return 0; ++} ++ ++ZTEST(net_socket_tls_api_extension, test_wolfssl_verify_cb_null_ctx) ++{ ++ int server_fd, client_fd, ret; ++ k_tid_t server_thread_id; ++ struct sockaddr_in sa; ++ const struct timespec ts = { ++ .tv_sec = 1704067200, ++ .tv_nsec = 0, ++ }; ++ ++ clock_settime(CLOCK_REALTIME, &ts); ++ ++ /* Register wolfSSL-style callback with ctx set to NULL. ++ * wolfSSL should NOT call wolfSSL_SetCertCbCtx, so ++ * store->userCtx in the callback should be NULL. ++ */ ++ struct tls_cert_verify_cb_wolfssl cb = { ++ .cb = wolfssl_verify_null_ctx_cb, ++ .ctx = NULL, ++ }; ++ ++ server_fd = test_configure_server(&server_thread_id, ++ TLS_PEER_VERIFY_NONE, false, false); ++ client_fd = test_configure_client(&sa, false, "localhost"); ++ ++ ret = zsock_setsockopt(client_fd, SOL_TLS, TLS_CERT_VERIFY_CALLBACK_WOLFSSL, ++ &cb, sizeof(cb)); ++ zassert_ok(ret, "failed to set TLS_CERT_VERIFY_CALLBACK_WOLFSSL (%d)", errno); ++ ++ ret = zsock_connect(client_fd, (struct sockaddr *)&sa, sizeof(sa)); ++ zassert_equal(ret, 0, ++ "connect failed -- userCtx may not be NULL (%d)", errno); ++ ++ test_shutdown(client_fd, server_fd, server_thread_id); ++} ++ ++/* --- Server-side wolfSSL-style verify callback test --- */ ++ ++static struct wolfssl_verify_ctx server_wolfssl_verify_ctx; ++ ++static void server_thread_wolfssl_verify_fn(void *arg0, void *arg1, void *arg2) ++{ ++ const int server_fd = POINTER_TO_INT(arg0); ++ int r; ++ int client_fd; ++ socklen_t addrlen; ++ struct sockaddr_in sa; ++ ++ k_thread_name_set(k_current_get(), "server"); ++ ++ memset(&sa, 0, sizeof(sa)); ++ addrlen = sizeof(sa); ++ ++ k_sem_give(&server_sem); ++ r = accept(server_fd, (struct sockaddr *)&sa, &addrlen); ++ zassert_not_equal(r, -1, "accept() failed (%d)", errno); ++ client_fd = r; ++ ++ r = close(client_fd); ++ zassert_not_equal(r, -1, "close() failed on the server fd (%d)", errno); ++} ++ ++ZTEST(net_socket_tls_api_extension, test_wolfssl_verify_cb_server_accept) ++{ ++ static const sec_tag_t server_tag_list[] = { ++ CA_CERTIFICATE_TAG, ++ SERVER_CERTIFICATE_TAG, ++ }; ++ int server_fd, client_fd, ret; ++ k_tid_t tid; ++ struct sockaddr_in sa; ++ /* Use REQUIRED, not OPTIONAL. OPTIONAL maps to ++ * WOLFSSL_VERIFY_DEFAULT which does not include ++ * WOLFSSL_VERIFY_PEER, so the server never requests a ++ * client cert and the verify callback is never invoked. ++ * REQUIRED includes WOLFSSL_VERIFY_PEER, causing the ++ * server to request and verify the client certificate. ++ */ ++ int peer_verify = TLS_PEER_VERIFY_REQUIRED; ++ const struct timespec ts = { ++ .tv_sec = 1704067200, ++ .tv_nsec = 0, ++ }; ++ ++ clock_settime(CLOCK_REALTIME, &ts); ++ ++ memset(&server_wolfssl_verify_ctx, 0, sizeof(server_wolfssl_verify_ctx)); ++ server_wolfssl_verify_ctx.decision = 1; /* accept */ ++ ++ struct tls_cert_verify_cb_wolfssl cb = { ++ .cb = wolfssl_verify_cb, ++ .ctx = &server_wolfssl_verify_ctx, ++ }; ++ ++ k_sem_init(&server_sem, 0, 1); ++ ++ /* Create server socket */ ++ ret = socket(AF_INET, SOCK_STREAM, IPPROTO_TLS_1_2); ++ zassert_not_equal(ret, -1, "failed to create server socket (%d)", errno); ++ server_fd = ret; ++ ++ int yes = true; ++ ++ ret = setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)); ++ zassert_not_equal(ret, -1, "failed to set SO_REUSEADDR (%d)", errno); ++ ++ ret = setsockopt(server_fd, SOL_TLS, TLS_SEC_TAG_LIST, ++ server_tag_list, sizeof(server_tag_list)); ++ zassert_not_equal(ret, -1, "failed to set TLS_SEC_TAG_LIST (%d)", errno); ++ ++ ret = setsockopt(server_fd, SOL_TLS, TLS_HOSTNAME, "localhost", ++ sizeof("localhost")); ++ zassert_not_equal(ret, -1, "failed to set TLS_HOSTNAME (%d)", errno); ++ ++ ret = setsockopt(server_fd, SOL_TLS, TLS_PEER_VERIFY, ++ &peer_verify, sizeof(peer_verify)); ++ zassert_not_equal(ret, -1, "failed to set TLS_PEER_VERIFY (%d)", errno); ++ ++ ret = setsockopt(server_fd, SOL_TLS, TLS_CERT_VERIFY_CALLBACK_WOLFSSL, ++ &cb, sizeof(cb)); ++ zassert_ok(ret, "failed to set TLS_CERT_VERIFY_CALLBACK_WOLFSSL (%d)", errno); ++ ++ memset(&sa, 0, sizeof(sa)); ++ sa.sin_addr.s_addr = INADDR_ANY; ++ sa.sin_family = AF_INET; ++ sa.sin_port = htons(PORT); ++ ++ ret = bind(server_fd, (struct sockaddr *)&sa, sizeof(sa)); ++ zassert_not_equal(ret, -1, "failed to bind (%d)", errno); ++ ++ ret = listen(server_fd, 1); ++ zassert_not_equal(ret, -1, "failed to listen (%d)", errno); ++ ++ tid = k_thread_create(&server_thread, server_stack, STACK_SIZE, ++ server_thread_wolfssl_verify_fn, ++ INT_TO_POINTER(server_fd), NULL, NULL, ++ K_PRIO_PREEMPT(8), 0, K_NO_WAIT); ++ ++ ret = k_sem_take(&server_sem, K_MSEC(TIMEOUT)); ++ zassert_equal(0, ret, "failed to synchronize with server thread (%d)", ret); ++ ++ /* Client presents its cert */ ++ client_fd = test_configure_client(&sa, true, "localhost"); ++ ++ ret = zsock_connect(client_fd, (struct sockaddr *)&sa, sizeof(sa)); ++ zassert_equal(ret, 0, "failed to connect (%d)", errno); ++ ++ /* The server's wolfSSL-style verify callback should have been invoked ++ * during accept() on the cloned child context. ++ */ ++ zassert_true(server_wolfssl_verify_ctx.cb_called, ++ "server wolfSSL-style verify callback was not called"); ++ ++ ret = close(client_fd); ++ zassert_not_equal(-1, ret, "close() failed on client fd (%d)", errno); ++ ret = close(server_fd); ++ zassert_not_equal(-1, ret, "close() failed on server fd (%d)", errno); ++ ret = k_thread_join(&server_thread, K_FOREVER); ++ zassert_equal(0, ret, "k_thread_join() failed (%d)", ret); ++ k_yield(); ++} ++ ++#endif /* CONFIG_WOLFSSL_VERIFY_CALLBACK */ ++ + ZTEST_SUITE(net_socket_tls_api_extension, NULL, setup, NULL, NULL, NULL); +diff --git a/tests/net/socket/tls_ext/testcase.yaml b/tests/net/socket/tls_ext/testcase.yaml +index 497e5f8c65e..787bf54050a 100644 +--- a/tests/net/socket/tls_ext/testcase.yaml ++++ b/tests/net/socket/tls_ext/testcase.yaml +@@ -8,3 +8,15 @@ tests: + platform_allow: qemu_x86 + integration_platforms: + - qemu_x86 ++ ++ net.socket.tls.ext.wolfssl: ++ platform_allow: native_sim ++ tags: net socket tls wolfssl ++ extra_args: ++ - OVERLAY_CONFIG=overlay-wolfssl.conf ++ ++ net.socket.tls.ext.wolfssl.verify_cb: ++ platform_allow: native_sim ++ tags: net socket tls wolfssl ++ extra_args: ++ - OVERLAY_CONFIG=overlay-wolfssl-verify-cb.conf +diff --git a/tests/subsys/random/rng/overlay-wolfssl.conf b/tests/subsys/random/rng/overlay-wolfssl.conf +new file mode 100644 +index 00000000000..c462890e3d1 +--- /dev/null ++++ b/tests/subsys/random/rng/overlay-wolfssl.conf +@@ -0,0 +1,8 @@ ++# Copyright (c) 2025 wolfSSL Inc. ++# SPDX-License-Identifier: Apache-2.0 ++ ++CONFIG_POSIX_API=y ++CONFIG_POSIX_TIMERS=y ++CONFIG_POSIX_THREADS=y ++CONFIG_WOLFSSL=y ++CONFIG_CTR_DRBG_CSPRNG_GENERATOR=y diff --git a/zephyr/README.md b/zephyr/README.md index e937f229..f83e2299 100644 --- a/zephyr/README.md +++ b/zephyr/README.md @@ -1,33 +1,13 @@ -## How to setup wolfSSL support for standard Zephyr TLS Sockets and RNG +## wolfSSL support for standard Zephyr TLS Sockets and RNG -wolfSSL can also be used as the underlying implementation for the default Zephyr TLS socket interface. -With this enabled, all existing applications using the Zephyr TLS sockets will now use wolfSSL inside -for all TLS operations. This will also enable wolfSSL as the default RNG implementation. To enable this -feature, first ensure wolfSSL has been added to the west manifest using the instructions from the -README.md here: https://github.com/wolfSSL/wolfssl/tree/master/zephyr - -Once the west manifest has been updated, run west update, then run the following command to patch the sources - -``` -patch -p1 < /path/to/your/osp/zephyr/zephyr-tls-{PATCH_VERSION}.patch -``` - -Where PATCH_VERSION is the appropriate patch version. - -### Run Zephyr TLS samples - -``` -west build -b samples/net/sockets/echo_server -DEXTRA_CONF_FILE=overlay-wolfssl.conf -``` - -### Run Zephyr TLS tests - -``` -west build -b tests/net/socket/tls_ext/ -DEXTRA_CONF_FILE=overlay-wolfssl.conf -``` - -``` -west build -b tests/net/socket/tls/ -DEXTRA_CONF_FILE=overlay-wolfssl.conf -``` +This directory contains the patches that enable wolfSSL as the underlying +implementation for Zephyr's default TLS socket interface, organized by +Zephyr release. +| Zephyr version | Patch and instructions | +|----------------|--------------------------------------------| +| 3.7 | [3.7/README.md](3.7/README.md) | +| 4.3 | [4.3/README.md](4.3/README.md) | +For general information on wolfSSL as a Zephyr module, see +https://github.com/wolfSSL/wolfssl/tree/master/zephyr. From 953622dbcf901ff28e44eb53c9b4175411be1202 Mon Sep 17 00:00:00 2001 From: Colton Willey Date: Mon, 20 Apr 2026 18:32:09 -0700 Subject: [PATCH 2/2] zephyr/4.3/README: add configuration, limitations, and references sections --- zephyr/4.3/README.md | 44 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/zephyr/4.3/README.md b/zephyr/4.3/README.md index 031ce009..9803134f 100644 --- a/zephyr/4.3/README.md +++ b/zephyr/4.3/README.md @@ -6,9 +6,9 @@ for all TLS operations. This will also enable wolfSSL as the default RNG impleme feature, first ensure wolfSSL has been added to the west manifest using the instructions from the README.md here: https://github.com/wolfSSL/wolfssl/tree/master/zephyr -This integration depends on the default Zephyr TLS support changes in the wolfSSL module. The required -changes are contained in wolfSSL after the merge of the associated default-TLS-support PR; use a wolfSSL -revision that includes those changes. +This integration depends on new Kconfig options added to the wolfSSL Zephyr module; use a wolfSSL +revision that includes the PR adding Zephyr 4.3 default TLS support (`WOLFSSL_SESSION_EXPORT`, +`WOLFSSL_KEEP_PEER_CERT`, `WOLFSSL_ALWAYS_VERIFY_CB`, and the `native_sim` timer gate extension). Once the west manifest has been updated, run west update, then run the following command to patch the sources @@ -16,6 +16,39 @@ Once the west manifest has been updated, run west update, then run the following patch -p1 < /path/to/your/osp/zephyr/4.3/zephyr-tls-4.3.0.patch ``` +### Minimum prj.conf + +Use `tests/net/socket/tls/overlay-wolfssl.conf` as a template. At minimum the application needs +`CONFIG_MBEDTLS=n`, `CONFIG_WOLFSSL=y`, and Zephyr POSIX support (`CONFIG_POSIX_API=y`, +`CONFIG_POSIX_TIMERS=y`, `CONFIG_POSIX_THREADS=y`). Size `CONFIG_COMMON_LIBC_MALLOC_ARENA_SIZE` +to the application footprint. + +### Configuration options + +Kconfig help text is authoritative: +- wolfSSL module: https://github.com/wolfSSL/wolfssl/blob/master/zephyr/Kconfig +- Zephyr TLS socket layer: `subsys/net/lib/sockets/Kconfig` (after applying the patch) + +Options added by this integration: + +| Kconfig | Purpose | +|---|---| +| `WOLFSSL_SESSION_EXPORT` | External session cache (serialize sessions across connections) | +| `WOLFSSL_KEEP_PEER_CERT` | Retain peer certificate after handshake | +| `WOLFSSL_ALWAYS_VERIFY_CB` | Invoke verify callback on success in addition to failure | +| `WOLFSSL_VERIFY_CALLBACK` | Enable wolfSSL-native per-cert verify callback via the `TLS_CERT_VERIFY_CALLBACK_WOLFSSL` socket option | + +Existing wolfSSL module options (`WOLFSSL_DTLS`, `WOLFSSL_ALPN`, `WOLFSSL_PSK`, +`WOLFSSL_TLS_VERSION_1_3`, `WOLFSSL_MAX_FRAGMENT_LEN`) are opt-in as usual. + +### Limitations + +- TLS 1.0 and 1.1 disabled (`NO_OLD_TLS`). +- The mbedTLS-style `TLS_CERT_VERIFY_CALLBACK` socket option is not supported on the wolfSSL backend. +- `TLS_CERT_NOCOPY` has no effect — certificates are always copied. +- TLS 1.3 0-RTT not wired on the wolfSSL path. +- OCSP and CRL handling is library-internal on both backends; there is no Zephyr socket-option API for it. + ### Run Zephyr TLS samples ``` @@ -31,3 +64,8 @@ west build -b tests/net/socket/tls_ext/ -DEXTRA_CONF_FILE=overlay-w ``` west build -b tests/net/socket/tls/ -DEXTRA_CONF_FILE=overlay-wolfssl.conf ``` + +### References + +- Zephyr TLS sockets: https://docs.zephyrproject.org/latest/connectivity/networking/api/sockets.html +- wolfSSL Zephyr module: https://github.com/wolfSSL/wolfssl/tree/master/zephyr