From 138e2e6983914ee3feca2301b17696cc75f74bf5 Mon Sep 17 00:00:00 2001 From: Vikram Dattu Date: Tue, 27 Feb 2024 19:06:34 +0530 Subject: [PATCH 1/2] Add support for closing provisioning window after PoP mismatch - The feature prevents brute force attacks by not allowing more than allowed PoP attempts - Allowed failure attempts are configurable via menuconfig and is 5 by default - Change added for both `app_wifi` and `app_wifi_with_homekit` - Min IDF version needed (release/v5.1.3) --- CHANGES.md | 3 ++ examples/common/app_wifi/Kconfig.projbuild | 11 ++++++ examples/common/app_wifi/app_wifi.c | 37 +++++++++++++++++++ examples/common/app_wifi/app_wifi.h | 2 + .../components/app_wifi/Kconfig.projbuild | 11 ++++++ .../app_wifi/app_wifi_with_homekit.c | 34 +++++++++++++++++ .../app_wifi/app_wifi_with_homekit.h | 2 + 7 files changed, 100 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 0b651846..6581dbe4 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,8 @@ # Changes +## 27-Feb-2024: Add support for closing provisioning window after PoP mismatch + - For ESP IDF v5.1.3 and later, provisioning will be stopped if there are 5 attempts to establish secure session with wrong PoP. This count can be set to any value between 0 and 20. 0 means that provisioning will not be stopped (which will be same as the earlier behaviour before this change). + ## 21-Nov-2022 (esp_rmaker_mqtt: Add MQTT budgeting to control the number of messages sent) - Due to some poor, non-optimised coding or bugs, it is possible that the node keeps bombarding the MQTT diff --git a/examples/common/app_wifi/Kconfig.projbuild b/examples/common/app_wifi/Kconfig.projbuild index 73c70526..ffe51917 100644 --- a/examples/common/app_wifi/Kconfig.projbuild +++ b/examples/common/app_wifi/Kconfig.projbuild @@ -6,6 +6,17 @@ menu "ESP RainMaker App Wi-Fi Provisioning" help Show the QR code for provisioning. + config APP_WIFI_PROV_MAX_POP_MISMATCH + int + default 5 + range 0 20 + prompt "Max wrong pop attempts allowed" + help + Set the maximum wrong pop attempts allowed before stopping provisioning. + Set 0 for the feature to be disabled. + This safeguards the device from brute-force attempt by limiting the wrong pop allowed. + Needs IDF version >= 5.1.3 + choice APP_WIFI_PROV_TRANSPORT bool "Provisioning Transport method" default APP_WIFI_PROV_TRANSPORT_BLE diff --git a/examples/common/app_wifi/app_wifi.c b/examples/common/app_wifi/app_wifi.c index ca90d654..93f71792 100644 --- a/examples/common/app_wifi/app_wifi.c +++ b/examples/common/app_wifi/app_wifi.c @@ -59,6 +59,13 @@ static esp_timer_handle_t prov_stop_timer; #define APP_WIFI_PROV_TIMEOUT_PERIOD CONFIG_APP_WIFI_PROV_TIMEOUT_PERIOD /* Autofetch period in micro-seconds */ static uint64_t prov_timeout_period = (APP_WIFI_PROV_TIMEOUT_PERIOD * 60 * 1000000LL); + +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 3) +#define APP_PROV_STOP_ON_CREDS_MISMATCH +#elif (CONFIG_APP_WIFI_PROV_MAX_RETRY_CNT > 0) +#warning "Provisioning window stop on max credentials failures, needs IDF version >= 5.1.3" +#endif + #ifdef CONFIG_APP_WIFI_SHOW_DEMO_INTRO_TEXT #define ESP_RAINMAKER_GITHUB_EXAMPLES_PATH "https://github.com/espressif/esp-rainmaker/blob/master/examples" @@ -155,6 +162,11 @@ static void event_handler(void* arg, esp_event_base_t event_base, #ifdef CONFIG_APP_WIFI_RESET_PROV_ON_FAILURE static int retries = 0; #endif + +#ifdef APP_PROV_STOP_ON_CREDS_MISMATCH + static int failed_cnt = 0; +#endif + if (event_base == WIFI_PROV_EVENT) { switch (event_id) { case WIFI_PROV_START: @@ -207,6 +219,28 @@ static void event_handler(void* arg, esp_event_base_t event_base, default: break; } +#ifdef APP_PROV_STOP_ON_CREDS_MISMATCH + } else if (event_base == PROTOCOMM_SECURITY_SESSION_EVENT) { + switch (event_id) { + case PROTOCOMM_SECURITY_SESSION_SETUP_OK: + ESP_LOGI(TAG, "Secured session established!"); + break; + case PROTOCOMM_SECURITY_SESSION_INVALID_SECURITY_PARAMS: + /* fall-through */ + case PROTOCOMM_SECURITY_SESSION_CREDENTIALS_MISMATCH: + ESP_LOGE(TAG, "Received incorrect PoP or invalid security params! event: %d", (int) event_id); + if (CONFIG_APP_WIFI_PROV_MAX_POP_MISMATCH && + (++failed_cnt >= CONFIG_APP_WIFI_PROV_MAX_POP_MISMATCH)) { + /* stop provisioning for security reasons */ + wifi_prov_mgr_stop_provisioning(); + ESP_LOGW(TAG, "Max PoP attempts reached! Provisioning disabled for security reasons. Please reboot device to restart provisioning"); + esp_event_post(APP_WIFI_EVENT, APP_WIFI_EVENT_PROV_CRED_MISMATCH, NULL, 0, portMAX_DELAY); + } + break; + default: + break; + } +#endif } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) { esp_wifi_connect(); } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { @@ -367,6 +401,9 @@ void app_wifi_init(void) /* Register our event handler for Wi-Fi, IP and Provisioning related events */ ESP_ERROR_CHECK(esp_event_handler_register(WIFI_PROV_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL)); +#ifdef APP_PROV_STOP_ON_CREDS_MISMATCH + ESP_ERROR_CHECK(esp_event_handler_register(PROTOCOMM_SECURITY_SESSION_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL)); +#endif ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL)); ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL)); diff --git a/examples/common/app_wifi/app_wifi.h b/examples/common/app_wifi/app_wifi.h index f6ac81a8..62451a14 100644 --- a/examples/common/app_wifi/app_wifi.h +++ b/examples/common/app_wifi/app_wifi.h @@ -37,6 +37,8 @@ typedef enum { APP_WIFI_EVENT_PROV_TIMEOUT, /** Provisioning has restarted due to failures (Invalid SSID/Passphrase) */ APP_WIFI_EVENT_PROV_RESTART, + /** Provisioning closed due to invalid credentials */ + APP_WIFI_EVENT_PROV_CRED_MISMATCH, } app_wifi_event_t; /** Types of Proof of Possession */ diff --git a/examples/homekit_switch/components/app_wifi/Kconfig.projbuild b/examples/homekit_switch/components/app_wifi/Kconfig.projbuild index b2955af8..04e7ccd9 100644 --- a/examples/homekit_switch/components/app_wifi/Kconfig.projbuild +++ b/examples/homekit_switch/components/app_wifi/Kconfig.projbuild @@ -6,6 +6,17 @@ menu "App Wi-Fi Provisioning" help Show the QR code for provisioning. + config APP_WIFI_PROV_MAX_POP_MISMATCH + int + default 5 + range 0 20 + prompt "Max wrong pop attempts allowed" + help + Set the maximum wrong pop attempts allowed before stopping provisioning. + Set 0 for the feature to be disabled. + This safeguards the device from brute-force attempt by limiting the wrong pop allowed. + Needs IDF version >= 5.1.3 + choice APP_WIFI_PROV_TRANSPORT bool "Provisioning Transport method" default APP_WIFI_PROV_TRANSPORT_BLE diff --git a/examples/homekit_switch/components/app_wifi/app_wifi_with_homekit.c b/examples/homekit_switch/components/app_wifi/app_wifi_with_homekit.c index 91e1e18d..6487f450 100644 --- a/examples/homekit_switch/components/app_wifi/app_wifi_with_homekit.c +++ b/examples/homekit_switch/components/app_wifi/app_wifi_with_homekit.c @@ -63,6 +63,12 @@ static esp_timer_handle_t prov_stop_timer; /* Autofetch period in micro-seconds */ static uint64_t prov_timeout_period = (APP_WIFI_PROV_TIMEOUT_PERIOD * 60 * 1000000LL); +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 3) +#define APP_PROV_STOP_ON_CREDS_MISMATCH +#elif (CONFIG_APP_WIFI_PROV_MAX_RETRY_CNT > 0) +#warning "Provisioning window stop on max credentials failures, needs IDF version >= 5.1.3" +#endif + static void app_wifi_print_qr(const char *name, const char *pop, const char *transport) { if (!name || !transport) { @@ -115,6 +121,9 @@ static void event_handler(void* arg, esp_event_base_t event_base, { #ifdef CONFIG_APP_WIFI_RESET_PROV_ON_FAILURE static int retries = 0; +#endif +#ifdef APP_PROV_STOP_ON_CREDS_MISMATCH + static int failed_cnt = 0; #endif if (event_base == WIFI_PROV_EVENT) { switch (event_id) { @@ -171,6 +180,28 @@ static void event_handler(void* arg, esp_event_base_t event_base, default: break; } +#ifdef APP_PROV_STOP_ON_CREDS_MISMATCH + } else if (event_base == PROTOCOMM_SECURITY_SESSION_EVENT) { + switch (event_id) { + case PROTOCOMM_SECURITY_SESSION_SETUP_OK: + ESP_LOGI(TAG, "Secured session established!"); + break; + case PROTOCOMM_SECURITY_SESSION_INVALID_SECURITY_PARAMS: + /* fall-through */ + case PROTOCOMM_SECURITY_SESSION_CREDENTIALS_MISMATCH: + ESP_LOGE(TAG, "Received incorrect PoP or invalid security params! event: %d", (int) event_id); + if (CONFIG_APP_WIFI_PROV_MAX_POP_MISMATCH && + (++failed_cnt >= CONFIG_APP_WIFI_PROV_MAX_POP_MISMATCH)) { + /* stop provisioning for security reasons */ + wifi_prov_mgr_stop_provisioning(); + ESP_LOGW(TAG, "Max PoP attempts reached! Provisioning disabled for security reasons. Please reboot device to restart provisioning"); + esp_event_post(APP_WIFI_EVENT, APP_WIFI_EVENT_PROV_CRED_MISMATCH, NULL, 0, portMAX_DELAY); + } + break; + default: + break; + } +#endif } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) { esp_wifi_connect(); } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_CONNECTED) { @@ -332,6 +363,9 @@ void app_wifi_with_homekit_init(void) ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL)); #endif ESP_ERROR_CHECK(esp_event_handler_register(WIFI_PROV_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL)); +#ifdef APP_PROV_STOP_ON_CREDS_MISMATCH + ESP_ERROR_CHECK(esp_event_handler_register(PROTOCOMM_SECURITY_SESSION_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL)); +#endif ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL)); ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_GOT_IP6, &event_handler, NULL)); diff --git a/examples/homekit_switch/components/app_wifi/app_wifi_with_homekit.h b/examples/homekit_switch/components/app_wifi/app_wifi_with_homekit.h index 3b715e1f..3fdbfcfd 100644 --- a/examples/homekit_switch/components/app_wifi/app_wifi_with_homekit.h +++ b/examples/homekit_switch/components/app_wifi/app_wifi_with_homekit.h @@ -20,6 +20,8 @@ typedef enum { APP_WIFI_EVENT_PROV_TIMEOUT, /** Provisioning has restarted due to failures (Invalid SSID/Passphrase) */ APP_WIFI_EVENT_PROV_RESTART, + /** Provisioning closed due to invalid credentials */ + APP_WIFI_EVENT_PROV_CRED_MISMATCH, } app_wifi_event_t; /** Types of Proof of Possession */ From d3024a12591e57d4ae765151a52f8a145f977d3d Mon Sep 17 00:00:00 2001 From: Vikram Dattu Date: Fri, 8 Mar 2024 17:29:41 +0530 Subject: [PATCH 2/2] Bump up esp_schedule patch version in idf_component.yml --- components/esp_schedule/idf_component.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/esp_schedule/idf_component.yml b/components/esp_schedule/idf_component.yml index bc5a3b33..870ec30c 100644 --- a/components/esp_schedule/idf_component.yml +++ b/components/esp_schedule/idf_component.yml @@ -1,5 +1,5 @@ ## IDF Component Manager Manifest File -version: "1.1.0" +version: "1.1.1" description: ESP Schedules, used in RainMaker url: https://github.com/espressif/esp-rainmaker/tree/master/components/esp_schedule repository: https://github.com/espressif/esp-rainmaker.git