diff --git a/components/bt/host/nimble/Kconfig.in b/components/bt/host/nimble/Kconfig.in index 5bd801e7569..390e8322557 100644 --- a/components/bt/host/nimble/Kconfig.in +++ b/components/bt/host/nimble/Kconfig.in @@ -202,6 +202,72 @@ menu "Security (SMP)" Enabling this option will delete the pairing of the device and stack will NOT post any event to application. If this option is disabled, application will get BLE_GAP_EVENT_REPEAT_PAIRING event. + + config BT_NIMBLE_SMP_HARDENED_REPAIRING + bool "Reject re-pairing that weakens an existing bond" + default y + help + When a bond already exists, refuse a new pairing that would lower MITM + protection, drop Secure Connections, or shorten the encryption key. + Also enforce that a Pairing Response keeps the MITM/SC bits announced + in a preceding Security Request. + + Same-level re-pairing (peer deleted its own bond and pairs again at the + same strength) is still allowed. A peer that must legitimately re-pair at + a lower level has to be unpaired first (ble_gap_unpair / NVS clear). + + Matches Bluedroid BT_BLE_SMP_HARDENED_REPAIRING. Disable only if a peer + must downgrade and the application cannot unbond it beforehand. + + config BT_NIMBLE_SMP_REQUIRE_ENC_BEFORE_REPAIR + bool "Require encryption before re-pairing with a bonded peer" + default n + help + When enabled, a bonded peer cannot start SMP pairing on an unencrypted + link. The stack replies with Pairing Failed and disconnects. This is + stricter than the Bluetooth Core Spec and stricter than Bluedroid; it + blocks same-level recovery when the peer deleted its bond and sends a + Pairing Request before encryption. + + Default is disabled so existing apps and one-sided bond-deletion + recovery keep working. Enable for maximum hardening against + unauthenticated re-pairing (BLERP V1/V2 style). + + config BT_NIMBLE_SMP_REMOVE_BOND_ON_PAIR_FAIL_AS_CENTRAL + bool "Remove the stored bond when pairing fails as Central" + default y + help + Erase NVS bonding keys when pairing or encryption fails while the local + device is Central (link-layer master). Historical Central behaviour and + the default: recovers when the peer deleted the bond and answers + encryption with Key Missing. + + A downgrade refused by BT_NIMBLE_SMP_HARDENED_REPAIRING never erases + the bond, regardless of this option. + + config BT_NIMBLE_SMP_REMOVE_BOND_ON_PAIR_FAIL_AS_PRPH + bool "Remove the stored bond when pairing fails as Peripheral" + default n + help + Erase NVS bonding keys when pairing or encryption fails while the local + device is Peripheral (link-layer slave). Default is disabled so a failed + pairing does not drop the bond; unbond explicitly from the app if needed. + + A downgrade refused by BT_NIMBLE_SMP_HARDENED_REPAIRING never erases + the bond, regardless of this option. + + config BT_NIMBLE_SMP_UNBOND_ON_KEY_MISSING + bool "Drop the local LE keys when the peer reports it has no key" + default n + help + As Central, an encryption attempt using the stored LTK can fail with + "PIN or Key Missing" when the peer deleted the bond. When enabled, the + local keys are discarded so re-pairing can proceed on the same link. + + Default is disabled: the bond is kept and the link is dropped, so a peer + cannot strip the stored security level by refusing to encrypt. With the + default, BT_NIMBLE_SMP_REMOVE_BOND_ON_PAIR_FAIL_AS_CENTRAL still recovers + Central-side Key Missing by erasing on the failure path. endif endmenu #SMP diff --git a/components/bt/host/nimble/nimble b/components/bt/host/nimble/nimble index 05e03d6ab1f..b3c38720cec 160000 --- a/components/bt/host/nimble/nimble +++ b/components/bt/host/nimble/nimble @@ -1 +1 @@ -Subproject commit 05e03d6ab1f502486a3aaf1e15e580847168b8b1 +Subproject commit b3c38720cecb56d6a36adde7be95fa0c70caa4e0 diff --git a/components/bt/host/nimble/port/include/esp_nimble_cfg.h b/components/bt/host/nimble/port/include/esp_nimble_cfg.h index e6b1e2d12c9..0a1de86ac8a 100644 --- a/components/bt/host/nimble/port/include/esp_nimble_cfg.h +++ b/components/bt/host/nimble/port/include/esp_nimble_cfg.h @@ -2241,6 +2241,51 @@ #endif #endif +/* Bool Kconfig: defined=1 when y, absent when n. Non-ESP builds keep syscfg defaults. */ +#ifndef MYNEWT_VAL_BLE_SMP_HARDENED_REPAIRING +#if defined(CONFIG_BT_NIMBLE_SMP_HARDENED_REPAIRING) +#define MYNEWT_VAL_BLE_SMP_HARDENED_REPAIRING (1) +#elif defined(CONFIG_BT_NIMBLE_ENABLED) +#define MYNEWT_VAL_BLE_SMP_HARDENED_REPAIRING (0) +#else +#define MYNEWT_VAL_BLE_SMP_HARDENED_REPAIRING (1) +#endif +#endif + +#ifndef MYNEWT_VAL_BLE_SMP_REQUIRE_ENC_BEFORE_REPAIR +#if defined(CONFIG_BT_NIMBLE_SMP_REQUIRE_ENC_BEFORE_REPAIR) +#define MYNEWT_VAL_BLE_SMP_REQUIRE_ENC_BEFORE_REPAIR (1) +#else +#define MYNEWT_VAL_BLE_SMP_REQUIRE_ENC_BEFORE_REPAIR (0) +#endif +#endif + +#ifndef MYNEWT_VAL_BLE_SMP_REMOVE_BOND_ON_PAIR_FAIL_AS_CENTRAL +#if defined(CONFIG_BT_NIMBLE_SMP_REMOVE_BOND_ON_PAIR_FAIL_AS_CENTRAL) +#define MYNEWT_VAL_BLE_SMP_REMOVE_BOND_ON_PAIR_FAIL_AS_CENTRAL (1) +#elif defined(CONFIG_BT_NIMBLE_ENABLED) +#define MYNEWT_VAL_BLE_SMP_REMOVE_BOND_ON_PAIR_FAIL_AS_CENTRAL (0) +#else +#define MYNEWT_VAL_BLE_SMP_REMOVE_BOND_ON_PAIR_FAIL_AS_CENTRAL (1) +#endif +#endif + +#ifndef MYNEWT_VAL_BLE_SMP_REMOVE_BOND_ON_PAIR_FAIL_AS_PERIPHERAL +#if defined(CONFIG_BT_NIMBLE_SMP_REMOVE_BOND_ON_PAIR_FAIL_AS_PRPH) +#define MYNEWT_VAL_BLE_SMP_REMOVE_BOND_ON_PAIR_FAIL_AS_PERIPHERAL (1) +#else +#define MYNEWT_VAL_BLE_SMP_REMOVE_BOND_ON_PAIR_FAIL_AS_PERIPHERAL (0) +#endif +#endif + +#ifndef MYNEWT_VAL_BLE_SMP_UNBOND_ON_KEY_MISSING +#if defined(CONFIG_BT_NIMBLE_SMP_UNBOND_ON_KEY_MISSING) +#define MYNEWT_VAL_BLE_SMP_UNBOND_ON_KEY_MISSING (1) +#else +#define MYNEWT_VAL_BLE_SMP_UNBOND_ON_KEY_MISSING (0) +#endif +#endif + #ifndef MYNEWT_VAL_BT_NIMBLE_MEM_OPTIMIZATION #ifdef CONFIG_BT_NIMBLE_MEM_OPTIMIZATION #define MYNEWT_VAL_BT_NIMBLE_MEM_OPTIMIZATION CONFIG_BT_NIMBLE_MEM_OPTIMIZATION diff --git a/components/esp_hid/src/nimble_hidh.c b/components/esp_hid/src/nimble_hidh.c index a062ebd0b2f..f5ea694daea 100644 --- a/components/esp_hid/src/nimble_hidh.c +++ b/components/esp_hid/src/nimble_hidh.c @@ -428,6 +428,9 @@ static void read_device_services(esp_hidh_dev_t *dev) dev->config.report_maps = NULL; goto done; } + /* HOGP defaults to Report Protocol Mode; the Protocol Mode + * characteristic is often write-only, so it cannot be read back. */ + memset(dev->protocol_mode, ESP_HID_PROTOCOL_MODE_REPORT, dev->config.report_maps_len); } for (uint16_t s = 0; s < svc_count; s++) { @@ -467,6 +470,7 @@ static void read_device_services(esp_hidh_dev_t *dev) for (uint16_t c = 0; c < ccount; c++) { cuuid = ble_uuid_u16(&char_result[c].uuid.u); chandle = char_result[c].val_handle; + report = NULL; ESP_LOGD(TAG, " CHAR:(%d), handle: %d, perm: 0x%02x, uuid: 0x%04x", c + 1, chandle, char_result[c].properties, cuuid); if (suuid == BLE_SVC_GAP_UUID16) { @@ -514,21 +518,22 @@ static void read_device_services(esp_hidh_dev_t *dev) } } } - continue; - } else { - if (cuuid == BLE_SVC_HID_CHR_UUID16_PROTOCOL_MODE) { - if (char_result[c].properties & BLE_GATT_CHR_PROP_READ) { - if (read_char(dev->ble.conn_id, chandle, &rdata, &rlen) == 0 && rlen) { - dev->protocol_mode[hidindex] = *((uint8_t *)rdata); - free(rdata); - rdata = NULL; - } + } + continue; + } else if (suuid == BLE_SVC_HID_UUID16) { + if (cuuid == BLE_SVC_HID_CHR_UUID16_PROTOCOL_MODE) { + if ((char_result[c].properties & BLE_GATT_CHR_PROP_READ) != 0 + && dev->protocol_mode != NULL && hidindex < dev->config.report_maps_len) { + if (read_char(dev->ble.conn_id, chandle, &rdata, &rlen) == 0 && rlen) { + dev->protocol_mode[hidindex] = *((uint8_t *)rdata); } + free(rdata); + rdata = NULL; } continue; - } - if (cuuid == BLE_SVC_HID_CHR_UUID16_REPORT_MAP) { - if (char_result[c].properties & BLE_GATT_CHR_PROP_READ) { + } else if (cuuid == BLE_SVC_HID_CHR_UUID16_REPORT_MAP) { + if ((char_result[c].properties & BLE_GATT_CHR_PROP_READ) != 0 + && dev->config.report_maps != NULL && hidindex < dev->config.report_maps_len) { if (read_char(dev->ble.conn_id, chandle, &rdata, &rlen) == 0 && rlen) { uint8_t *copy = nimble_hidh_dup_bytes(rdata, rlen); if (copy) { @@ -537,41 +542,38 @@ static void read_device_services(esp_hidh_dev_t *dev) dev->config.report_maps[hidindex].len = rlen; } } - continue; - } else if (cuuid == BLE_SVC_HID_CHR_UUID16_BOOT_KBD_INP || cuuid == BLE_SVC_HID_CHR_UUID16_BOOT_KBD_OUT - || cuuid == BLE_SVC_HID_CHR_UUID16_BOOT_MOUSE_INP || cuuid == BLE_SVC_HID_CHR_UUID16_RPT) { - report = (esp_hidh_dev_report_t *)malloc(sizeof(esp_hidh_dev_report_t)); - if (report == NULL) { - ESP_LOGE(TAG, "malloc esp_hidh_dev_report_t failed"); - goto done; - } - report->next = NULL; - report->permissions = char_result[c].properties; - report->handle = chandle; - report->ccc_handle = 0; - report->report_id = 0; - report->map_index = hidindex; - if (cuuid == BLE_SVC_HID_CHR_UUID16_BOOT_KBD_INP) { - report->protocol_mode = ESP_HID_PROTOCOL_MODE_BOOT; - report->report_type = ESP_HID_REPORT_TYPE_INPUT; - report->usage = ESP_HID_USAGE_KEYBOARD; - report->value_len = 8; - } else if (cuuid == BLE_SVC_HID_CHR_UUID16_BOOT_KBD_OUT) { - report->protocol_mode = ESP_HID_PROTOCOL_MODE_BOOT; - report->report_type = ESP_HID_REPORT_TYPE_OUTPUT; - report->usage = ESP_HID_USAGE_KEYBOARD; - report->value_len = 8; - } else if (cuuid == BLE_SVC_HID_CHR_UUID16_BOOT_MOUSE_INP) { - report->protocol_mode = ESP_HID_PROTOCOL_MODE_BOOT; - report->report_type = ESP_HID_REPORT_TYPE_INPUT; - report->usage = ESP_HID_USAGE_MOUSE; - report->value_len = 8; - } else { - report->protocol_mode = ESP_HID_PROTOCOL_MODE_REPORT; - report->report_type = 0; - report->usage = ESP_HID_USAGE_GENERIC; - report->value_len = 0; - } + free(rdata); + rdata = NULL; + } + continue; + } else if (cuuid == BLE_SVC_HID_CHR_UUID16_BOOT_KBD_INP || cuuid == BLE_SVC_HID_CHR_UUID16_BOOT_KBD_OUT + || cuuid == BLE_SVC_HID_CHR_UUID16_BOOT_MOUSE_INP || cuuid == BLE_SVC_HID_CHR_UUID16_RPT) { + report = (esp_hidh_dev_report_t *)malloc(sizeof(esp_hidh_dev_report_t)); + if (report == NULL) { + ESP_LOGE(TAG, "malloc esp_hidh_dev_report_t failed"); + goto done; + } + report->next = NULL; + report->permissions = char_result[c].properties; + report->handle = chandle; + report->ccc_handle = 0; + report->report_id = 0; + report->map_index = hidindex; + if (cuuid == BLE_SVC_HID_CHR_UUID16_BOOT_KBD_INP) { + report->protocol_mode = ESP_HID_PROTOCOL_MODE_BOOT; + report->report_type = ESP_HID_REPORT_TYPE_INPUT; + report->usage = ESP_HID_USAGE_KEYBOARD; + report->value_len = 8; + } else if (cuuid == BLE_SVC_HID_CHR_UUID16_BOOT_KBD_OUT) { + report->protocol_mode = ESP_HID_PROTOCOL_MODE_BOOT; + report->report_type = ESP_HID_REPORT_TYPE_OUTPUT; + report->usage = ESP_HID_USAGE_KEYBOARD; + report->value_len = 8; + } else if (cuuid == BLE_SVC_HID_CHR_UUID16_BOOT_MOUSE_INP) { + report->protocol_mode = ESP_HID_PROTOCOL_MODE_BOOT; + report->report_type = ESP_HID_REPORT_TYPE_INPUT; + report->usage = ESP_HID_USAGE_MOUSE; + report->value_len = 8; } else { report->protocol_mode = ESP_HID_PROTOCOL_MODE_REPORT; report->report_type = 0; @@ -581,6 +583,8 @@ static void read_device_services(esp_hidh_dev_t *dev) } else { continue; } + } else { + continue; } struct ble_gatt_dsc descr_result[HIDH_MAX_DSCS]; uint16_t num_dsc = HIDH_MAX_DSCS;