espressif / esp-nimble

A fork of NimBLE stack, for use with ESP32 and ESP-IDF
Apache License 2.0
76 stars 49 forks source link

LTK is not deleted when peer uses random address and our address is public #44

Closed dalvisachin closed 4 months ago

dalvisachin commented 2 years ago

I have ESP32C3 acting as a client. The peer is NRF52832. ESP32C3 is using public address where as NRF is using random address.

My application requires bonding with just works pairing.

At very first connection everything works as expected.

Now Peer (NRF52) disconnects and in ESP32 I get the event BLE_GAP_EVENT_DISCONNECT in which I call ble_gap_unpair() to unpair with this peer and deleting the bonding info, keys etc.

When I want to connect again with this Peer, i expect ESP32 to send the pairing request again and start the bonding process fresh. But instead it send encryption request. After digging deeper I found when I make call to function ble_gap_security_initiate() in BLE_GAP_EVENT_CONNECT

it always finds the LTK for this peer.

below is the snippet from function ble_gap_security_initiate() in file ble_gap.c

if (rc == 0 && value_sec.ltk_present) {
            // ----> value_sec.ltk_present is always true here but after unpairing with peer it should be false <----//
            rc = ble_sm_enc_initiate(conn_handle, value_sec.key_size,
                                     value_sec.ltk, value_sec.ediv,
                                     value_sec.rand_num,
                                     value_sec.authenticated);
            if (rc != 0) {
                goto done;
            }
        } else {
            rc = ble_sm_pair_initiate(conn_handle);
            if (rc != 0) {
                goto done;
            }
        }
    }

This problem does not occur with Peers who advertise public address.

After reading this https://github.com/espressif/esp-nimble/pull/7 and this https://github.com/espressif/esp-nimble/issues/8

I make a call to ble_hs_pvcy_rpa_config() with parameter NIMBLE_HOST_ENABLE_NRPA since my local host is not using RPA but only the Peer uses RPA.

the function blecent_on_sync() is as follows

static void blecent_on_sync(void)
{
    int rc;

    rc = ble_hs_util_ensure_addr(0);

    rc = ble_hs_pvcy_rpa_config(NIMBLE_HOST_ENABLE_NRPA);

    ESP_LOGW(CONFIG_OUTPUT_TAG_BLE, "ble_hs_pvcy_rpa_config(NIMBLE_HOST_ENABLE_NRPA);");
    assert(rc == 0);

    blecent_scan(BLE_HS_FOREVER);

}

the function blecent_scan() is as follows

static void blecent_scan(int32_t duration)
{
    uint8_t own_addr_type = BLE_OWN_ADDR_PUBLIC;
    struct ble_gap_disc_params disc_params;
    int rc;

     /* Printing ADDR */
    uint8_t addr_val[6] = {0};
    rc = ble_hs_id_copy_addr(own_addr_type, addr_val, NULL);
    MODLOG_DFLT(INFO, "Device Address: ");
    print_addr(addr_val);

    /* Tell the controller to filter duplicates; we don't want to process
     * repeated advertisements from the same device.
     */
    disc_params.filter_duplicates = 1;

    /**
     * Perform a passive scan.  I.e., don't send follow-up scan requests to
     * each advertiser.
     */
    disc_params.passive = 1;

    /* Use defaults for the rest of the parameters. */
    disc_params.itvl = 0;
    disc_params.window = 0;
    disc_params.filter_policy = 0;
    disc_params.limited = 0;

    rc = ble_gap_disc(own_addr_type, duration, &disc_params,
                      blecent_gap_event, NULL);
    if (rc != 0) {
        MODLOG_DFLT(ERROR, "Error initiating GAP discovery procedure; rc=%d\n",
                    rc);
    }
}

SM configuration for ESP32C3 is

ble_hs_cfg.sm_io_cap = BLE_SM_IO_CAP_NO_IO;
ble_hs_cfg.sm_bonding = 1;
ble_hs_cfg.sm_mitm = 0;
ble_hs_cfg.sm_sc = 0;
ble_hs_cfg.sm_keypress = 0;
ble_hs_cfg.sm_our_key_dist   = BLE_SM_PAIR_KEY_DIST_ENC | BLE_SM_PAIR_KEY_DIST_ID;
ble_hs_cfg.sm_their_key_dist = BLE_SM_PAIR_KEY_DIST_ENC | BLE_SM_PAIR_KEY_DIST_ID;

Can someone please help me with this ? Or tell me if I am doing something wrong? My goal is to have public address and bond to peer with random address, when this peer disconnects the connection then remove the bonding info and start pairing process again.

Like I already mentioned everything works fine if if peer has public address.

rahult-github commented 2 years ago

Hi @dalvisachin ,

ESP32C3 has controller based privacy enabled by default.

I make a call to ble_hs_pvcy_rpa_config() with parameter NIMBLE_HOST_ENABLE_NRPA since my local host is not using RPA but only the Peer uses RPA.

This functionality is basically host based privacy. Enabling both at same time should not be done. So, i suggest do not enable host based privacy for ESP32C3.

Can you please share the idf version being used ? Also, if you have any debug enabled logs, then that would be helpful to understand the flow of commands / events.

Thanks, Rahul

dalvisachin commented 2 years ago

Hello @rahult-github, Thank you for replying.

I removed the host based privacy and tried again but no luck.

I have attached the sources, sdkconfig and logs as well. I am using esp-idf v4.4.1, I have also tried v4.3.2 but it has same behavior. The source is based on blecent example.

I added some debug logs in function ble_gap_security_initiate() in file ble_gap.c to check if LTK is found or not. And for the peer even after it is disconnected and ble_gap_unpair() is called, LTK for this peer is always found in subsequent connection attempts.

if (conn_flags & BLE_HS_CONN_F_MASTER) {
        /* Search the security database for an LTK for this peer.  If one
         * is found, perform the encryption procedure rather than the pairing
         * procedure.
         */
        rc = ble_store_read_peer_sec(&key_sec, &value_sec);
        if (rc == 0 && value_sec.ltk_present) {
            MODLOG_DFLT(WARN, "Found LTK for %02X:%02X:%02X:%02X:%02X:%02X", 
                        addrs.peer_id_addr.val[5], addrs.peer_id_addr.val[4],
                        addrs.peer_id_addr.val[3], addrs.peer_id_addr.val[2],
                        addrs.peer_id_addr.val[1], addrs.peer_id_addr.val[0]
                        );

            rc = ble_sm_enc_initiate(conn_handle, value_sec.key_size,
                                     value_sec.ltk, value_sec.ediv,
                                     value_sec.rand_num,
                                     value_sec.authenticated);
            if (rc != 0) {
                goto done;
            }
        } else {
            MODLOG_DFLT(WARN, "NO LTK found for %02X:%02X:%02X:%02X:%02X:%02X", 
                        addrs.peer_id_addr.val[5], addrs.peer_id_addr.val[4],
                        addrs.peer_id_addr.val[3], addrs.peer_id_addr.val[2],
                        addrs.peer_id_addr.val[1], addrs.peer_id_addr.val[0]
                        );
            rc = ble_sm_pair_initiate(conn_handle);
            if (rc != 0) {
                goto done;
            }
        }
    }

Steps to reproduce-

  1. Create a peer which advertises random address
  2. use ESP32C3 to connect to this peer
  3. now from peer side break the connection
  4. start peer advertising again without resetting esp32

I also checked with BLE sniffer and found out that, ESP32 sends encryption requests (since it finds LTK) for all subsequent connections and peer then terminates the connection since it expects pairing request.

Thanks, Sachin

logs.zip main.zip sdkconfig.zip

rahult-github commented 5 months ago

ble_gap_unpair was updated to delete IRK / LTK for various scenarios. That should help fix this issue.