This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

sd_ble_gap_encrypt() does not seem to encrypt link after bonding done

Hello,

I'm working with a nrf51822, SoftDevice S120 v2.0 and SDK v9.0 platform. Specifically using it a Central role to connect to a commercial Blood Pressure peripheral.

My peripheral has 2 distinct "modes":

  1. Pairing Mode - here is where you need to pair and bond to it. In this mode you can only bond and access certain protected characteristics (like Battery Level). I have to press a special button to put it into this Pairing mode.

  2. Reading Mode - here is where you just need to encrypt and get the readings from all it's protected characteristics

I have managed to successfully go through Pairing mode and create a bond thanks to the help I got from this post: devzone.nordicsemi.com/.../

As part of the successful authenticating and bonding process in Pairing Mode, sd_ble_gap_encrypt() was called and BLE_GAP_EVT_CONN_SEC_UPDATE was returned with :

p_ble_evt->evt.gap_evt.params.conn_sec_update.conn_sec.sec_mode.lv = 2

This indicted a successful encryption and it went on and completed authenticating and bonding. Bond information was then saved in the central. During this phase as the link was encrypted I was also able to read the protected Battery Level characteristic.

I then put the Peripheral in Reading Mode: Everything seems to flow fine (i.e. the Peripheral is matched to the Bond and the device manager puts it straight into Encryption using bond information). This flow happens:

  • Central finds Peripheral and Connects
  • get DM_EVT_DEVICE_CONTEXT_LOADED
  • get DM_EVT_SECURITY_SETUP (from the peripheral to request security process to start)
  • call dm_security_setup_req()
  • dm_security_setup_req() says "Already bonded device, encrypt the link."
  • sd_ble_gap_encrypt() is then called
  • get DM_EVT_SECURITY_SETUP with the values:

p_ble_evt->evt.gap_evt.params.conn_sec_update.conn_sec.sec_mode.lv = 1;

p_ble_evt->evt.gap_evt.params.conn_sec_update.conn_sec.sec_mode.sm = 1;

(m_connection_table[index].state & STATE_BONDED) = 0x08;

  • it then flags that the .lv is not 2 and goes into the "Lost bond case, generate a security refresh event!" flow which calls sd_ble_gap_authenticate() again due to start_sec_procedure being set to true
  • The process then errors out with the BLE_GAP_SEC_STATUS_AUTH_REQ error (probably because the Peripheral does not allow for pairing and bonding when in Reading Mode).

I have tried a few things already but have had no success, I have tried:

  • I ignored the the fact that BLE_GAP_EVT_CONN_SEC_UPDATE returned sec_mode.lv = 1 and forced it to continue with the process (i.e. making the DM go into the DM_EVT_LINK_SECURED block) and then tried to read the Battery Level of the Peripheral, but as expected it said "BLE_GATT_STATUS_ATTERR_INSUF_AUTHENTICATION"

The problem seems to be that, when using bond info to sd_ble_gap_encrypt() it does not seem to work.

Also not sure if its relevant or not but my sec_params in DM when authenticating/bonding was:

SEC_PARAM_BOND  1. 
SEC_PARAM_MITM  0. 
SEC_PARAM_IO_CAPABILITIES  BLE_GAP_IO_CAPS_NONE. 
SEC_PARAM_OOB  0. 
SEC_PARAM_MIN_KEY_SIZE   7.  
SEC_PARAM_MAX_KEY_SIZE  16.

Also based on the "GAP Central Encryption Establishment using stored keys" MSC developer.nordicsemi.com/.../a00780.html when BLE_GAP_EVT_CONN_SEC_UPDATE is sent by SD it means that the link was encrypted using the LTK or it was rejected due to a missing key. Not sure how to detect the failure reason.

Any idea what may be wrong?

Thanks very much.

Parents
  • I suspect that you are hitting Variant #2 in the MSC.

    I''m not sure how much you know about bonding and re-encrypting the link, so I'll try to explain a bit first.

    The last phase of the bonding procedure is distribution of keys that can be used to encrypt the link in future connections and to resolve a resolvable private device address. The peripheral typically distributes the following keys:

    • Long Term Key (LTK) – a 128-bit key used to encrypt the link to a bonded device
    • Encryption Diversifier (EDIV) – a 16-bit value used to identify the LTK to encrypt the link to a bonded device
    • Random Number (Rand) - a 64-bit value used to identify the LTK to encrypt the link to a bonded device

    If the central and/or peripheral uses a resolvable private device address following key is usually distributed to the peer:

    • Identity Resolving Key (IRK) – a 128-bit key used to generate and resolve random addresses

    The central sends an encryption request with EDIV and Rand to the bonded peripheral, and it uses these to determine what LTK to use in order to encrypt the connection.

    Variant #2 will happen if the peripheral is not able to identify what LTK to use.

    Then you would get BLE_GAP_EVT_CONN_SEC_UPDATE with security level 1, and DM will try to bond again.

    I'm not sure why the peripheral can't identify what LTK to use, but I would start by checking that the EDIV and Rand that you receive from the peripheral are equal to the EDIV and Rand that you provide to the peripheral when you re-encrypt.

    Actually, what I would try before that is to add register_param.sec_param.kdist_central.id = 1; to device_manager_init(). Then the central will distribute its IRK, and it could be that the peripheral somehow uses this to identify the central, and what LTK to use.

Reply
  • I suspect that you are hitting Variant #2 in the MSC.

    I''m not sure how much you know about bonding and re-encrypting the link, so I'll try to explain a bit first.

    The last phase of the bonding procedure is distribution of keys that can be used to encrypt the link in future connections and to resolve a resolvable private device address. The peripheral typically distributes the following keys:

    • Long Term Key (LTK) – a 128-bit key used to encrypt the link to a bonded device
    • Encryption Diversifier (EDIV) – a 16-bit value used to identify the LTK to encrypt the link to a bonded device
    • Random Number (Rand) - a 64-bit value used to identify the LTK to encrypt the link to a bonded device

    If the central and/or peripheral uses a resolvable private device address following key is usually distributed to the peer:

    • Identity Resolving Key (IRK) – a 128-bit key used to generate and resolve random addresses

    The central sends an encryption request with EDIV and Rand to the bonded peripheral, and it uses these to determine what LTK to use in order to encrypt the connection.

    Variant #2 will happen if the peripheral is not able to identify what LTK to use.

    Then you would get BLE_GAP_EVT_CONN_SEC_UPDATE with security level 1, and DM will try to bond again.

    I'm not sure why the peripheral can't identify what LTK to use, but I would start by checking that the EDIV and Rand that you receive from the peripheral are equal to the EDIV and Rand that you provide to the peripheral when you re-encrypt.

    Actually, what I would try before that is to add register_param.sec_param.kdist_central.id = 1; to device_manager_init(). Then the central will distribute its IRK, and it could be that the peripheral somehow uses this to identify the central, and what LTK to use.

Children
  • Thanks very much for the clear explanation @Petter

    I had some success this morning, as I was using a forked codebase that my device firmware is built on, I worked out that for some reason it had removed the following lines in its sec_params setup:

    register_param.sec_param.kdist_periph.enc = 1; 
    register_param.sec_param.kdist_periph.id = 1;
    

    I noticed these lines in your example main.c in your ble_app_hrs_c_iOS_peripheral_with_fix package and added it back.

    After this I seem to have been able to successfully encrypt after bonding. I still need to confirm it all worked as I still have some difficulty accessing protected characteristics. Once confirmed I will mark this as answered.

Related