diff --git a/esphome/components/esp32/__init__.py b/esphome/components/esp32/__init__.py index b7faccaed6..fccf0ed09f 100644 --- a/esphome/components/esp32/__init__.py +++ b/esphome/components/esp32/__init__.py @@ -55,6 +55,7 @@ from .const import ( # noqa KEY_ESP32, KEY_EXTRA_BUILD_FILES, KEY_FLASH_SIZE, + KEY_FULL_CERT_BUNDLE, KEY_PATH, KEY_REF, KEY_REPO, @@ -670,6 +671,7 @@ CONF_FREERTOS_IN_IRAM = "freertos_in_iram" CONF_RINGBUF_IN_IRAM = "ringbuf_in_iram" CONF_HEAP_IN_IRAM = "heap_in_iram" CONF_LOOP_TASK_STACK_SIZE = "loop_task_stack_size" +CONF_USE_FULL_CERTIFICATE_BUNDLE = "use_full_certificate_bundle" # VFS requirement tracking # Components that need VFS features can call require_vfs_select() or require_vfs_dir() @@ -695,6 +697,18 @@ def require_vfs_dir() -> None: CORE.data[KEY_VFS_DIR_REQUIRED] = True +def require_full_certificate_bundle() -> None: + """Request the full certificate bundle instead of the common-CAs-only bundle. + + By default, ESPHome uses CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_CMN which + includes only CAs with >1% market share (~51 KB smaller than full bundle). + This covers ~99% of websites including Let's Encrypt, DigiCert, Google, Amazon. + + Call this from components that need to connect to services using uncommon CAs. + """ + CORE.data[KEY_ESP32][KEY_FULL_CERT_BUNDLE] = True + + def _parse_idf_component(value: str) -> ConfigType: """Parse IDF component shorthand syntax like 'owner/component^version'""" # Match operator followed by version-like string (digit or *) @@ -776,6 +790,9 @@ FRAMEWORK_SCHEMA = cv.Schema( min=8192, max=32768 ), cv.Optional(CONF_ENABLE_OTA_ROLLBACK, default=True): cv.boolean, + cv.Optional( + CONF_USE_FULL_CERTIFICATE_BUNDLE, default=False + ): cv.boolean, } ), cv.Optional(CONF_COMPONENTS, default=[]): cv.ensure_list( @@ -1093,6 +1110,18 @@ async def to_code(config): cg.add_build_flag("-Wno-nonnull-compare") + # Use CMN (common CAs) bundle by default to save ~51KB flash + # CMN covers CAs with >1% market share (~99% of websites) + # Components needing uncommon CAs can call require_full_certificate_bundle() + use_full_bundle = conf[CONF_ADVANCED].get( + CONF_USE_FULL_CERTIFICATE_BUNDLE, False + ) or CORE.data[KEY_ESP32].get(KEY_FULL_CERT_BUNDLE, False) + add_idf_sdkconfig_option( + "CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_FULL", use_full_bundle + ) + if not use_full_bundle: + add_idf_sdkconfig_option("CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_CMN", True) + add_idf_sdkconfig_option(f"CONFIG_IDF_TARGET_{variant}", True) add_idf_sdkconfig_option( f"CONFIG_ESPTOOLPY_FLASHSIZE_{config[CONF_FLASH_SIZE]}", True diff --git a/esphome/components/esp32/const.py b/esphome/components/esp32/const.py index 2a9456db23..9f8165818b 100644 --- a/esphome/components/esp32/const.py +++ b/esphome/components/esp32/const.py @@ -12,6 +12,7 @@ KEY_REFRESH = "refresh" KEY_PATH = "path" KEY_SUBMODULES = "submodules" KEY_EXTRA_BUILD_FILES = "extra_build_files" +KEY_FULL_CERT_BUNDLE = "full_cert_bundle" VARIANT_ESP32 = "ESP32" VARIANT_ESP32C2 = "ESP32C2" diff --git a/esphome/components/http_request/__init__.py b/esphome/components/http_request/__init__.py index 7347b8ebf7..07bc758037 100644 --- a/esphome/components/http_request/__init__.py +++ b/esphome/components/http_request/__init__.py @@ -165,6 +165,16 @@ async def to_code(config): ca_cert_content = f.read() cg.add(var.set_ca_certificate(ca_cert_content)) else: + # Uses the certificate bundle configured in esp32 component. + # By default, ESPHome uses the CMN (common CAs) bundle which covers + # ~99% of websites including GitHub, Let's Encrypt, DigiCert, etc. + # If connecting to services with uncommon CAs, components can call: + # esp32.require_full_certificate_bundle() + # Or users can set in their config: + # esp32: + # framework: + # advanced: + # use_full_certificate_bundle: true esp32.add_idf_sdkconfig_option( "CONFIG_MBEDTLS_CERTIFICATE_BUNDLE", True ) diff --git a/tests/components/esp32/test.esp32-idf.yaml b/tests/components/esp32/test.esp32-idf.yaml index 0e220623a1..d38cdfe2fd 100644 --- a/tests/components/esp32/test.esp32-idf.yaml +++ b/tests/components/esp32/test.esp32-idf.yaml @@ -7,6 +7,7 @@ esp32: enable_lwip_mdns_queries: true enable_lwip_bridge_interface: true disable_libc_locks_in_iram: false # Test explicit opt-out of RAM optimization + use_full_certificate_bundle: false # Test CMN bundle (default) wifi: ssid: MySSID