espressif / esp-idf

Espressif IoT Development Framework. Official development framework for Espressif SoCs.
Apache License 2.0
13.84k stars 7.32k forks source link

BLE Security – RPA Address Whitelist / Accept List (IDFGH-6995) #8613

Open beeboopx opened 2 years ago

beeboopx commented 2 years ago

Is your feature request related to a problem? Please describe.

It is a highly desirable BLE security feature to only allow an Accept List of peer devices to scan and/or connect to the ESP32.

Currently, ESP32 only allows for Accept Listing non-RPA peer addresses. The issue has previously been reported here, circa 2018: https://github.com/espressif/esp-idf/issues/2262

It is clear that RPA addresses are unsupported for Accept Listing by inspecting esp_ble_wl_addr_type_t only enumerating BLE_WL_ADDR_TYPE_PUBLIC and BLE_WL_ADDR_TYPE_RANDOM type addresses: https://github.com/espressif/esp-idf/blob/2f9d47c708f39772b0e8f92d147b9e85aa3a0b19/components/bt/host/bluedroid/api/include/api/esp_bt_defs.h#L108-L112

However, looking few lines up in same file, its seen that esp_ble_addr_type_t additionally enumerates BLE_ADDR_TYPE_RPA_PUBLIC and BLE_ADDR_TYPE_RPA_RANDOM address types: https://github.com/espressif/esp-idf/blob/2f9d47c708f39772b0e8f92d147b9e85aa3a0b19/components/bt/host/bluedroid/api/include/api/esp_bt_defs.h#L100-L106

Indeed, bonding with RPA esp_ble_addr_type_t addresses is working successfully. Initial pairing is performed by physical button control invoking limited 30-second open advertisement to BLE_ADDR_TYPE_RPA_PUBLIC peer addresses, and only restricted advertisement outside this open window to BLE_ADDR_TYPE_RPA_RANDOM addresses (bonded peers). However, even during restricted advertising, any peer can still scan and connect to the ESP32. An unbonded peer that has connected during restricted advertising is only booted from connection in the event of custom disconnect we fire based on events raised by ESP_GAP_BLE_AUTH_CMPL_EVT and ESP_GATTS_CONNECT_EVT handlers during connection process.

This allows an unauthenticated peer during restricted advertising mode to hog the ESP32 BLE stack, repeatedly attempting connections to ESP32 and leading to DOS (Denial of Service) to legitimately bonded peers. Particularly the case when one has limited max BLE connections to 1 peer at a time.

Tried setting advertising filter param to ADV_FILTER_ALLOW_SCAN_WLST_CON_WLST after having successfully bonded with peer and thereafter adding bonded peer to Accept List tried using both:

esp_ble_gap_update_whitelist(true, m_peer_addr, BLE_WL_ADDR_TYPE_RANDOM); or esp_ble_gap_update_whitelist(true, m_peer_addr, BLE_WL_ADDR_TYPE_PUBLIC);

however this is never successful at allowing subsequent scan requests or connections with bonded peers, because peers are using RPA addresses. So unfortunately must always use the ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY advertising filter param and allow unauthenticated scan requests and connections.

Here's where the RPA address types get rejected in the IDF Accept List code: https://github.com/espressif/esp-idf/blob/2f9d47c708f39772b0e8f92d147b9e85aa3a0b19/components/bt/host/bluedroid/stack/btm/btm_ble_bgconn.c#L271-L287

Describe the solution you'd like

Would like to utilize the BLE Accept List functionality with RPA addresses which is the common BLE address implementation for modern smartphones to protect smartphone user privacy.

It would be nice-to-have after successful ESP_GAP_BLE_AUTH_CMPL_EVT to have optional configuration for auto-add any new bonded device into Accept List.

Describe alternatives you've considered

Additional context

Seems after 4 years of initial issue report, this important security vulnerability hasn't been solved. Raising this as a new issue in 2022 to shed additional light on it :)

There are probably additional complexities involved with functionality to allow RPA addresses into Accept List which is why i'm assuming it wasn't implemented at same time as the non-RPA addresses. Maybe there are underlying software or hardware limitations that are difficult to overcome? Anyways would be good to learn more about any of the challenges involved.

Appreciate Espressif and all the repo contributors' support, thank you in advance!

arjaanda commented 2 years ago

@fillybrese I am facing exactly the same issue. When we contacted espressif they provided us a solution where we can try to manually filter the devices at the application level. The idea is to basically store the address received during 'ESP_GAP_BLE_AUTH_CMPL_EVT' as a bonded device. During successive 'ESP_GATTS_CONNECT_EVT' events the address then received is compared against the bonded device list to allow or drop connection.

My problem with this approach is that in our application we use an android device as client and the comparison fails in the 'ESP_GATTS_CONNECT_EVT' even though same device was bonded previously. This is of course due to changing addresses(RPA). Do you know is there is a way to get identity address at the application level to identify the device uniquely even with random address. Looking at your post it seemed like you were successful with this approach. Any help is appreciated.

mvgijssel commented 2 years ago

Would also be interested in this to enable tracking off Apple devices on ESPHome nodes. Without the RPA it’s hard to make sure you are tracking the right hardware.

veneno-529 commented 1 year ago

@fillybrese I am facing exactly the same issue. When we contacted espressif they provided us a solution where we can try to manually filter the devices at the application level. The idea is to basically store the address received during 'ESP_GAP_BLE_AUTH_CMPL_EVT' as a bonded device. During successive 'ESP_GATTS_CONNECT_EVT' events the address then received is compared against the bonded device list to allow or drop connection.

My problem with this approach is that in our application we use an android device as client and the comparison fails in the 'ESP_GATTS_CONNECT_EVT' even though same device was bonded previously. This is of course due to changing addresses(RPA). Do you know is there is a way to get identity address at the application level to identify the device uniquely even with random address. Looking at your post it seemed like you were successful with this approach. Any help is appreciated.

Did you find any solution to your problem? I'm facing the same issue.

xyzzy42 commented 10 months ago

I've tested this with the NimBLE stack and it can work to some extent. If the device was previously bonded and the IRK was shared, then the random RPA address can be resolved to the Identity Address (i.e., the "secret" public address of the phone). The address is resolved when the connection handle given the BLE_GAP_EVENT_CONNECT event is queried with ble_gap_conn_find(). So the identity address is knowable when the connect event callback is run and can be disconnected if not on an allowed list of bonded devices.

But one must realize that the connect event means connection complete. It is not possible to reject the connection before it is established. This leads to an easy DoS by repeatedly connecting, as described in the original post.

To ignore the connection request it would be necessary to use whitelisting. My understanding is that there are two types of privacy implementations: controller based or host based. In controller based the IRK(s) and Identity Addresses are sent to the controller and it can do the RPA resolving, so it is possible to a put a peer using a random RPA on a whitelist via its IA and IRK. In host based, the IRKs are only known to the host and so RPA resolving must be done there. The controller whitelist is not useful since it would need to contain the unknown random RPA address being used. As soon as the phone changed to a new RPA it would no longer be on the whitelist.

I think Espressif uses host based privacy and this is why the controller whitelist is not useful and there is no way to allow connections from modern phones without being vulnerable to an easy DoS. But despite a number of issues and forum posts about this over the years, they never seem to answer them.

esp-cjh commented 9 months ago

@xyzzy42 Yes. For ESP32 it use host based privacy on bluedroid because the controller of ESP32 doesn't support device privacy mode. If you want to use controller based privacy (reslove RPA and filter device with whitelist by controller), you can enable this config CONFIG_BT_BLE_RPA_SUPPORTED by menuconfig. But it should be noted that since the esp32 only support network privacy mode, it can only accept RPA and can't accept identity address.