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.

Reply
  • 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.

Children
  • 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)?

  • Hi,

    The BLE Heart Rate Collector Example supports bonding out of the box (but without MITM), so you can refer to that. You can refer to the message sequence charts in the SoftDevice specifications to see the detailed sequence diagrams for bonding on the peripheral and central side.

    Regarding the error you get on the central I do not know which example you have based this on (as the hart rate collector already supports bonding) and what changes you have done, but generally you will get NRF_ERROR_INVALID_STATE returned from sd_ble_gatts_exchange_mtu_reply() if either the connection handle is invalid, or if there is no pending ATT_MTU request. Have you added code for handling this request in your application? If so, it could be that there is a conflict if, as this is normally hanled by the GATT module if that is used (components/ble/nrf_ble_gatt/nrf_ble_gatt.c). If you also handle the request in in your application, that would mean two calls to sd_ble_gatts_exchange_mtu_reply() for a single request, which is not valid.

  • I am using my central app for this development (not a Nordic example). This app has been used reliably for connecting to unsecure heart rate services for years. I am now attempting to introduce security for certain devices that now require it.

    I have confirmed that there is no conflict from multiple attempts to reply to the MTU request. You can see an image of the call stack on the nRF52832 below where the nrf_ble_gatt_on_ble_evt handler is processing the request.

    I have also included a snippet of a BT capture showing the behavior. No replay ever appears on the link.

  • Hi,

    I see. I am not able to find any other reasons for getting NRF_ERROR_INVALID_STATE in this case (other than there not being any pending request or the connection handle not being for an active connection). Can you double check that this is the error you get, and that it is from sd_ble_gatts_exchange_mtu_reply? And which event to you get before calling it? (I am not sure, as the sniffer trace screenshot also shows a LL_LENGTH_REQ, which is not the same as a GATT length update, and for that you reply with sd_ble_gap_data_length_update() - perhaps that is mixed up somehow in your code)?

    There is no obvious link between this and encrypting the link though, and even though I see in the sniffer trace that the link is about to be encrypted, this could happen in parallel (the main limitation is that there can only be one LL procedure initiated by each side at a single time, but at least form the screenshot that does not seem to be a problem). Does it help if you delay encrypting the link, just as a test? If not, perhaps you can share your code, and/or elaborate on which changes you did when this issues was introduced?

  • I will investigate further and come back to you.

    I was also curious whether the bonding operation also stores the discovered services/characteristics for the peer? If so, does this mean repeated discovery is not necessary? Can you reference which sdk element manages storing and retrieving the discovery results?

Related