Creating a Heart Rate Service Peripheral Example Application That Requires Mode 1 Level 3 Security

I am using SDK v15.3 and would like to modify the peripheral heart rate service example to require the Central to establish mode 1, level 3 security. I am undecided on whether it will be numeric comparison, passkey or OOB, but would like to evaluate each.

If there is another example I could reference to help with the changes to the heart rate service example, please advise.

Parents
  • Hi,

    In Bluetooth, the security level is set per characteristic. Referring to the Heart Rate example in nRF5 SDK 15.3, you can see that the security level of the heart rate characteristics is set in services_init():

        // Here the sec level for the Heart Rate Service can be changed/increased.
        hrs_init.hrm_cccd_wr_sec = SEC_OPEN;
        hrs_init.bsl_rd_sec      = SEC_OPEN;

    If you want to use level 3, change this to SEC_MITM intend of SEC_OPEN. With this, security level 3 is required in order to access the specific characteristics. Note that the mode essentially means that MITM or OOB is used, so you do not need to do other changes in this part regardless of if you are using numeric comparison, passkey or OOB.

    The second part is to expand the example to use the MITM protection method you want to use/test. For that, you can refer to other examples and copy-past in relevant parts. For instance, ble_app_gls use passkey (with the nRF being the display), printing the passkey over UART. Which MITM method that works is depending on the I/O capabilities of your product. If it has both a keyboard and a display, you can use numeric comparison (only with LE secure connections and provided the peer also supports it). If it has a keyboard or a display, it can use passkey, provided the peer has the opposite (or both). For OOB you would typically need something application specific.

  • I found the hrm_ccd_wr_sec setting and changed it to SEC_JUST_WORKS as a start. This resulted in an "Insuffcient Authentication" error when the Central attempted to enable the characteristic notification.

    I then enabled the peer manager functionality in the Central to manage the bonding process. That all worked as expected. I am now struggling with the reconnection after both Central and Peripheral have stored the bonding information. I can see that the Central successfully reaches the sd_ble_gap_encrypt() function; however, the peripheral is sending an ATTMTU request prior to the Central enabling encryption and the Central attempts to reply but receives an NRF_ERROR_INVALID_STATE error in response to sd_ble_gatts_exchange_mtu_reply().

    I haven't  changed any of the Central logic which was in place prior to supporting security, so typically the nrf_ble_gatt_on_ble_evt() would handle the ATT requests/responses and my application event handler would just start service discovery on the CONNECT event. I suspect this has to change somehow.

    Can you point me to a sequence diagram that demonstrates a reconnection between bonded Central and Peripheral (and an example)?

  • I did have a follow up question related to central and peripheral bonding data becoming of "lost". If the central has it's bonding information, but the peripheral no longer does.

    From my BT capture I see that the central sends a LL_ENC_REQ and the peripheral responds with LL_ENC_RSP (which must have something in it that doesn't match. The peripheral then responds with LL_REJECT_IND. I don't see this handled in the SDK and wonder what is the correct action?

    Similarly, if the central no longer has the bonding information, but the peripheral does what is the expected behavior?

  • The default behavior is to reject pairing attempts from peers where there already exists a bond. This is for security reasons, as if you allow that, it means that an attacker can spoof the address of the peer you are bonded with, and replace that bond. It is however possible to allow re-pairing by BT_SMP_ALLOW_UNAUTH_OVERWRITE if you want to. Sometimes this may be required from a usability perspective, but it is important to be aware of the security implications.

  • The BT_SMP_ALLOW_UNAUTH_OVERWRITE doesn't pertain to SDK 15.3 I don't believe (at least I don't see any reference to that type of functionality).

    Specifically, I purposely deleted the bonding information from the peripheral to see the behavior so I'm trying to understand what the correct procedure is for forcing a re-bonding if either the central or peripheral loses the non-volatile storage holding the bonding information. Obviously, in our app on the central side I can delete the peer information (e.g., pm_peers_delete) which should force a re-bonding. What can I do from the central side if I have lost the bonding information but the peripheral has not? I'm just trying to understand what the spec says should occur in these instances.

  • Hi,

    Sorry, forgot the context over the weekend. The default behavior of the nRF5 SDK as well  is to reject re-pairing attempts for the reasons I explained in my previous points. You can allow re-pairing on that device too though, by doing as described in this post.

    mlyonupdesigns said:
    What can I do from the central side if I have lost the bonding information but the peripheral has not? I'm just trying to understand what the spec says should occur in these instances.

    The spec does not describe how this should be handled, it is up to the product designer. Generally, it is not a good idea to allow re-pairing. But for many small embedded devices it is needed. In the case you describe here, where the bonding information is lost on the central, the only way to recover is if the peripheral either has some mechanism to delete bonding data (for instance with a button press or similar), or if you allow re-pairing. It is commonly done, but as it is a potential security issue, it is prohibited by default in both the nRF5 SDK and nRF Connect SDK.

  • As described in the "post" you cite...will the LL_REJECT_IND sent by the peripheral trigger the PM_EVT_CONN_SEC_CONFIG_REQ, or more specifically, how is the central informed the the LL_REJECT_IND has been sent (this is received by the soft device, but I don't see how it bubbles up)?

Reply Children
  • No, it is not like that. If the central initiate pairing/bonding of the link, and the peripheral already have a bond with this central, you will get the PM_EVT_CONN_SEC_CONFIG_REQ event on the peripheral side. If you allow re-pairing by calling pm_conn_sec_config_reply() as shown in the mentioned post, a new pairing procedure will start. If not, the peripheral will send LL_REJECT_IND. If the device that initiated bonding (central in this case) is an nRF, you will get a PM_EVT_CONN_SEC_FAILED event at this point (when it has received the LL_REJECT_IND).

  • I have everything working now...thanks for the assistance.

    I am running into an issue whenever an initial bonding operation occurs unrelated to the central connection with the heart rate service peripheral. My implementation also supports a single peripheral connection which is interacts with the central connection in the following way:

    1. Peripheral advertising is enabled by default

    a. Initiating advertising first sets the Tx power (sd_ble_gap_tx_power_set), updates the advertising message (sd_ble_gap_adv_set_configure) and finally starts the advertising (ble_advertising_start)

    2. Upon starting to scan for heart rate service peripherals, the peripheral advertising is stopped (sd_ble_gap_adv_stop)

    3. After connecting, bonding and enabling the heart rate service notification, the peripheral advertising is enabled again.

    The above sequence worked perfectly prior to adding the bonding functionality. It works when connecting to an already bonded heart rate peripheral. However, it now fails reliably after step 3 above as follows:

    ...when calling sd_ble_gap_adv_set_configure() it returns NRF_ERROR_INVALID_STATE (see code snippet below). It doesn't matter whether the third parameter is NULL or not. I have confirmed that advertising is definitely being stopped and that I am calling the below function once prior to re-starting adverstising.

    ble_gap_adv_data_t adv_data;
    memcpy(&adv_data, p_profile->padvertising->p_adv_data, sizeof(ble_gap_adv_data_t));
    if (p_profile->padvertising->adv_mode_current == BLE_ADV_MODE_IDLE)
    {
    result = sd_ble_gap_adv_set_configure(&p_profile->padvertising->adv_handle, &adv_data, &p_profile->padvertising->adv_params);
    if (result != NRF_SUCCESS)
    asm("nop");
    }
    else
    {
    result = sd_ble_gap_adv_set_configure(&p_profile->padvertising->adv_handle, &adv_data, NULL);
    if (result != NRF_SUCCESS)
    asm("nop");
    }

    I am willing to create a separate support question if that is desired.

  • You can disregard the last question...I have figured out the issue.

    You can consider this question closed.

Related