Connection security level is stuck at level 1 after it had successfully paired (with confirmed connection security level 4) with an iPhone.

Hi,

My development environment:

- VS Code

- nRF Connect SDK: v2.7.0

Description:

- My firmware had successfully paired (with passcode) with an iPhone. Security level reaches level 4 as expected. BLE advertising is setup with accept_list to make sure only the paired Central can connect.

- After paired, it works for a few hours.

- Sometimes only : when reconnecting, security level is only level 1, and stick to level 1 forever.

- When the connection security level is stick to level 1, printing its connection info crashes the app although `bt_conn_get_info()` return success. Please refer to `print_connection_params()` below.

Outline of my code:

int main(void)
{
    ...
    int err = init_ecg_bluetooth();
	if (err)
	{
		LOG_ERR("bt_enable error returned: %d", err);
		return 0;
	}
	...
}

static struct bt_conn *current_conn = NULL;

static void on_connected(struct bt_conn *conn, uint8_t err)
{
    if (err)
    {
        LOG_ERR("Connection failed (err 0x%02x)\n", err);
    }
    else
    {
        LOG_INF("Connected\n");

        current_conn = bt_conn_ref(conn);
        print_connection_params(current_conn);
    }
}

static void on_disconnected(struct bt_conn *conn, uint8_t reason)
{
    // C:\ncs\v2.4.0\zephyr\include\zephyr\bluetooth\hci_err.h
    LOG_INF("Disconnected (reason 0x%02x)\n", reason);

    bt_conn_unref(current_conn);
    current_conn = NULL;
    ...
}

static void on_security_changed(struct bt_conn *conn, bt_security_t level, enum bt_security_err error)
{
    if (error != BT_SECURITY_ERR_SUCCESS)
    {
        LOG_ERR("failed to upgrade security: %d", error);
        ...
        return;
    }

    ...

    LOG_INF("connection security upgraded: %d -> %d", level, is_conn_secured());

    ...
}

/**
 * @brief This only gets called for the first pairing.
 *        Since then, only @ref on_security_changed() is called.
 *
 * @param conn
 * @param bonded
 */
static void on_pairing_complete(struct bt_conn *conn, bool bonded)
{
    LOG_INF("bonded: %d", bonded);
    if (bonded)
    {
        ...

        bt_foreach_bond(BT_ID_DEFAULT, copy_last_bond_addr, NULL);
        print_addr_le(bond_addr, "bonded addr");

        ...
    }
}

static void on_pairing_failed(struct bt_conn *conn, enum bt_security_err reason)
{
    LOG_ERR("pairing failed: %d", reason);

    ...
}

void print_connection_params(struct bt_conn *conn)
{
    struct bt_conn_info info;
    int err = bt_conn_get_info(conn, &info);
    if (err < 0)
    {
        LOG_ERR("Failed to get connection info: %d", err); // this line never prints!
    }
    else
    {
        LOG_INF("Connection params: interval %d*1.25ms, latency %d, timeout %d*10ms, security: %d",
                info.le.interval, info.le.latency, info.le.timeout,
                info.security.level); // ! crash here! this line never prints
    }
}

// Function below is triggered on a button press
void print_ecg_ble_state()
{
    print_addr_le(bond_addr, "bonded addr"); // this line prints
    ...
    
    if (current_conn)
    {
        LOG_INF("conn available with security level: %d %d %d",
                bt_conn_get_security(current_conn),
                is_streaming, // our app variable
                conn_param); // this line prints: 1 0 2
        print_connection_params(current_conn);
    }
    else
    {
        LOG_INF("conn not available"); // this does not print
    }

    ...
}

My questions:

  1. Why only sometimes, on reconnecting, BLE stack fails to upgrade the security level from 1 to 4? It sticks to level 1 forever until the system crashes (causing reboot). After reboot, the system works as normal. This symptom is confirmed when this app works with both iPhone and ESP32S3 acting as Central.
  2. As you may notice, `print_connection_params()` is called with `current_conn` in `on_connected()`. After the connection is stick with level 1 forever (error state), calling `print_connection_params()` on the referenced `current_conn` crashes the app. Why is this? 
  3. Connection upgrade is done at the nRF BLE stack level, is connection forever stick to security level 1 because something's wrong with this nRF SDK Connect v2.7.0?

Best regards,

Quan

Parents
  • Hi Amanda,

    Of course, we did have those settings turned on to get the pairing feature work:

    CONFIG_SETTINGS=y
    CONFIG_BT_SETTINGS=y
    CONFIG_FLASH=y
    CONFIG_FLASH_PAGE_LAYOUT=y
    CONFIG_FLASH_MAP=y
    CONFIG_NVS=y

    After it had paired with a Central (an iPhone or ESP32S3), it worked for a few hours (power-resetting the board still gets the Central reconnected successfully). Just sometimes, the connection is stuck at level 1, the Central is not populated with connected event (such as in iOS, Bluetooth Settings, the device is shown as Not Connected). In this error state, if I print out the connection info, application crash as I had mentioned above.   

    Just a reminder: since we do use Accept list feature, there is no other device can connect to it, except for the paired Central (my device only accepts 1 pair:

    `CONFIG_BT_MAX_PAIRED=1`

    )

  • It sticks to level 1 forever until the system crashes (causing reboot).

    Could you provide the log?

  • Amanda,

    I have minimized the log printout into the attached screenshot. This log is printed when a single click on a button is triggered.

    When the single click is triggered, this function is called: 

    void print_ecg_ble_state()
    {
        print_data(sys_data, SYS_DATA_TOTAL_LEN, "sys_data");
    
        print_addr_le(bond_addr, "bonded addr");
        if (is_bonded()) // LED is currently off
        {
            led_start_blinking(LED_BLINK_CHECK_ALIVE, 3000);
        }
        // else, LED is blinking with pattern @ref LED_BLINK_PATTERN_BOND_CLEARED
    
        if (current_conn)
        {
            LOG_INF("conn available with security level: %d %d %d",
                    bt_conn_get_security(current_conn),
                    is_streaming,
                    conn_param);
            print_connection_params(current_conn); // it goes here!!!
        }
        else
        {
            LOG_INF("conn not available");
        }
    
        print_remaining_time(&hold_adv_timer, "hold_adv_timer");
        LOG_INF("pairing attempt: %d", pairing_attempt_count);
    
        LOG_INF("NAND info\n\taddr: 0x%08x, mode: %d, pktnum: %d",
                NAND_Addr,
                NAND_mode,
                Pkt_num);
        if (NAND_mode == 1)
        {
            LOG_INF("NOfflen: %d", NOff_len);
        }
    }
    
    void print_connection_params(struct bt_conn *conn)
    {
        struct bt_conn_info info;
        int err = bt_conn_get_info(conn, &info);
        if (err < 0)
        {
            LOG_ERR("Failed to get connection info: %d", err); // this line does not print!
        }
        else
        {
            LOG_INF("Connection params: interval %d*1.25ms, latency %d, timeout %d*10ms, security: %d",
                    info.le.interval, info.le.latency, info.le.timeout,
                    info.security.level);
        }
    }

    As in the green annotation line, `current_conn` is available with the printed security level at level 1.

    `current_conn` is only established and referenced by `bt_conn_ref()` in `on_connected()` event. And `bt_conn_unref()` in `on_disconnected()` event

    (Please refer to my original post for how I used it)

    I can confirm that while in this error state, no other BLE scanner can "see" it (my device only accepts 1 connection).

    When connection is made, the BLE stack should have been transitioned its security level from 1 to 4 without any interactions from the app. But in this error state, nothing happened after connection is established between the iPhone Central and my device (acting as a Peripheral).

    What could have happened?

    Best regards,

  • Hi Amanda,

    `settings_load()`: yes, we did have to call it to get the pairing feature work.

    The Exercise 2 and the github resource: we build on this sample code.

    The problem is: somehow, sometime, the SDK failed to transition the connection security level from 1 to 4.

    To keep it simple for you, I outlined the code that you may need to track down the problem (if I had misused some libraries) and you may observe via the log that: when the BLE stack falls into this error state, the connection becomes invalidated; accessing any members of its struct crashes our application. After paired, transitioning connection security level is done by the BLE stack. Please correct me if I'm wrong.

    What could be the problem?

    Best regards,

  • Hi, 

    If bonding information is deleted on one side but not the other, or if the stack is not configured to allow re-pairing, the security upgrade can fail. The default behaviour is not to allow re-pairing with just works. If you want this to be allowed, you can set CONFIG_BT_SMP_ALLOW_UNAUTH_OVERWRITE=y." 

    As for the crash, you can refer to this DevAcademy course https://academy.nordicsemi.com/courses/nrf-connect-sdk-intermediate/lessons/lesson-2-debugging/ to debug.  

    -Amanda H.

Reply Children
  • No, this is not bonding deleted at one side (either Central or peripheral); and as I had repeated a few times in the earlier replies, this connection security level is 4, i.e. Authenticated LE Secure Connections (MITM, ECDH), it's not Just-Work, i.e. level 2. So, the upper part of your reply is not applicable to our case.

    We would take a look at the debug tutorial, which might be helpful.

    Regards

Related