Keep TLS for components that re-include esp-tls and gate the sdkconfig escape hatch on value

This commit is contained in:
J. Nick Koston
2026-08-28 22:23:49 -05:00
parent 7d26655eb4
commit 72dbe4edbc
3 changed files with 58 additions and 9 deletions
+21 -8
View File
@@ -2333,13 +2333,23 @@ async def _reconcile_certificate_bundle_sdkconfig() -> None:
set_idf_sdkconfig_default("CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_CMN", True)
# User sdkconfig_options that mean "keep TLS on"; each becomes a request_tls().
# User sdkconfig_options that mean "keep TLS on" when set to y.
_MBEDTLS_TLS_ON_OPTIONS = (
"CONFIG_MBEDTLS_TLS_ENABLED",
"CONFIG_MBEDTLS_TLS_SERVER_AND_CLIENT",
"CONFIG_MBEDTLS_TLS_SERVER_ONLY",
"CONFIG_MBEDTLS_TLS_CLIENT_ONLY",
)
# Any user option under these prefixes only makes sense with TLS compiled in.
_TLS_OPTION_PREFIXES = ("CONFIG_ESP_TLS_", "CONFIG_MBEDTLS_SSL_", "CONFIG_ESP_HTTPS_")
def _user_sdkconfig_wants_tls(options: dict[str, Any]) -> bool:
return any(
(name in _MBEDTLS_TLS_ON_OPTIONS and value == "y")
or name.startswith(_TLS_OPTION_PREFIXES)
for name, value in options.items()
)
@coroutine_with_priority(CoroPriority.FINAL)
@@ -2352,8 +2362,14 @@ async def _reconcile_mbedtls_sdkconfig() -> None:
"""
data = _mbedtls_sdkconfig()
idf6 = idf_version() >= cv.Version(6, 0, 0)
# A component that re-includes esp-tls on its own (external components
# predating request_tls()) wants TLS just as much as a request_tls() call.
tls_required = (
data.tls_required
or "esp-tls" not in CORE.data[KEY_ESP32][KEY_EXCLUDE_COMPONENTS]
)
if not CORE.using_arduino and not data.tls_required:
if not CORE.using_arduino and not tls_required:
# IDF 6 made CONFIG_MBEDTLS_TLS_ENABLED a normal bool; on IDF 5 it has
# no prompt and is only reachable through the "None" TLS role choice.
if idf6:
@@ -2363,7 +2379,8 @@ async def _reconcile_mbedtls_sdkconfig() -> None:
# Enterprise WiFi selects TLS back on; wifi writes this itself, but
# esp_wifi can also be in the build without a wifi: block (openthread).
set_idf_sdkconfig_default("CONFIG_ESP_WIFI_ENTERPRISE_SUPPORT", False)
# WiFi, Bluetooth and secure boot re-select ECP through Kconfig.
# WiFi (ESP_WIFI_MBEDTLS_CRYPTO), Bluetooth and signed apps
# (SECURE_SIGNED_APPS) select ECP back on through Kconfig.
if not data.ecp_required:
set_idf_sdkconfig_default("CONFIG_MBEDTLS_ECP_C", False)
set_idf_sdkconfig_default("CONFIG_MBEDTLS_PEM_WRITE_C", False)
@@ -3089,11 +3106,7 @@ async def to_code(config):
# so it still gets the CMN variant pinned.
if conf[CONF_SDKCONFIG_OPTIONS].get("CONFIG_MBEDTLS_CERTIFICATE_BUNDLE") == "y":
require_certificate_bundle()
# A TLS role or esp-tls option in sdkconfig_options means the user relies on TLS.
elif any(
name in _MBEDTLS_TLS_ON_OPTIONS or name.startswith("CONFIG_ESP_TLS_")
for name in conf[CONF_SDKCONFIG_OPTIONS]
):
if _user_sdkconfig_wants_tls(conf[CONF_SDKCONFIG_OPTIONS]):
request_tls()
# Components from YAML are added in a separate coroutine with FINAL priority
@@ -0,0 +1,9 @@
esphome:
name: test
esp32:
board: esp32dev
framework:
type: esp-idf
sdkconfig_options:
CONFIG_MBEDTLS_TLS_ENABLED: n
+28 -1
View File
@@ -433,7 +433,7 @@ _IDF6 = cv.Version(6, 0, 0)
@pytest.mark.parametrize(
("framework", "idf", "data", "preset", "expected"),
("framework", "idf", "data", "preset", "expected", "excluded"),
[
pytest.param(
PlatformFramework.ESP32_IDF,
@@ -441,8 +441,19 @@ _IDF6 = cv.Version(6, 0, 0)
MbedtlsSdkconfigData(),
{},
{**_TLS_OFF_IDF5, **_PEER_CERT_PKCS7_OFF},
{"esp-tls"},
id="idf5_no_tls_user",
),
pytest.param(
# An external component that only re-included esp-tls keeps TLS.
PlatformFramework.ESP32_IDF,
_IDF5,
MbedtlsSdkconfigData(),
{},
_PEER_CERT_PKCS7_OFF,
set(),
id="idf_esp_tls_reincluded",
),
pytest.param(
PlatformFramework.ESP32_IDF,
_IDF6,
@@ -454,6 +465,7 @@ _IDF6 = cv.Version(6, 0, 0)
"CONFIG_MBEDTLS_SHA384_C": False,
"CONFIG_MBEDTLS_SHA512_C": False,
},
{"esp-tls"},
id="idf6_drops_sha512",
),
pytest.param(
@@ -462,6 +474,7 @@ _IDF6 = cv.Version(6, 0, 0)
MbedtlsSdkconfigData(sha512_required=True),
{},
{**_TLS_OFF_IDF6, **_PEER_CERT_PKCS7_OFF},
{"esp-tls"},
id="idf6_sha512_required",
),
pytest.param(
@@ -470,6 +483,7 @@ _IDF6 = cv.Version(6, 0, 0)
MbedtlsSdkconfigData(tls_required=True),
{},
_PEER_CERT_PKCS7_OFF,
{"esp-tls"},
id="idf_tls_requested",
),
pytest.param(
@@ -485,6 +499,7 @@ _IDF6 = cv.Version(6, 0, 0)
"CONFIG_MBEDTLS_X509_CSR_PARSE_C": False,
**_PEER_CERT_PKCS7_OFF,
},
{"esp-tls"},
id="idf_ecp_without_tls",
),
pytest.param(
@@ -497,6 +512,7 @@ _IDF6 = cv.Version(6, 0, 0)
"CONFIG_MBEDTLS_ECP_C": RawSdkconfigValue("y"),
**_PEER_CERT_PKCS7_OFF,
},
{"esp-tls"},
id="idf_user_ecp_wins",
),
pytest.param(
@@ -509,6 +525,7 @@ _IDF6 = cv.Version(6, 0, 0)
"CONFIG_MBEDTLS_SSL_KEEP_PEER_CERTIFICATE": True,
"CONFIG_MBEDTLS_PKCS7_C": True,
},
{"esp-tls"},
id="idf_peer_cert_pkcs7_required",
),
pytest.param(
@@ -517,6 +534,7 @@ _IDF6 = cv.Version(6, 0, 0)
MbedtlsSdkconfigData(disable_peer_cert=False, disable_pkcs7=False),
{},
_TLS_OFF_IDF5,
{"esp-tls"},
id="idf_advanced_disables_off",
),
pytest.param(
@@ -525,6 +543,7 @@ _IDF6 = cv.Version(6, 0, 0)
MbedtlsSdkconfigData(),
{},
_PEER_CERT_PKCS7_OFF,
{"esp-tls"},
id="arduino_keeps_tls",
),
],
@@ -536,6 +555,7 @@ def test_reconcile_mbedtls_sdkconfig(
data: MbedtlsSdkconfigData,
preset: dict[str, Any],
expected: dict[str, Any],
excluded: set[str],
) -> None:
"""The FINAL-priority reconciler turns TLS off only when nothing requested it;
user sdkconfig_options always win."""
@@ -544,6 +564,7 @@ def test_reconcile_mbedtls_sdkconfig(
KEY_IDF_VERSION: idf,
KEY_SDKCONFIG_OPTIONS: dict(preset),
KEY_MBEDTLS_SDKCONFIG: data,
KEY_EXCLUDE_COMPONENTS: excluded,
}
asyncio.run(_reconcile_mbedtls_sdkconfig())
@@ -571,6 +592,12 @@ def test_reconcile_mbedtls_sdkconfig(
),
pytest.param("tls_sdkconfig_esp_tls.yaml", False, False, id="raw_esp_tls"),
pytest.param("tls_sdkconfig_tls_role.yaml", False, False, id="raw_tls_role"),
# A role option set to n is not a request.
pytest.param(
"tls_sdkconfig_tls_enabled_n.yaml", True, True, id="raw_tls_enabled_n"
),
# SECURE_SIGNED_APPS selects ECP back on in Kconfig; ESPHome still writes the default.
pytest.param("signed_ota_ecdsa256_c6.yaml", True, True, id="signed_ota_ecdsa"),
],
)
def test_tls_disabled_sdkconfig(