Beware that this post is related to an SDK in maintenance mode
More Info: Consider nRF Connect SDK for new designs
This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

BLE Man-in-the-middle protection, multiple passkeys

Hello all,

I am developing a BLE application with the Softdevices, the peer management module, and with MITM protection.

 I've set a bonding static passkey as shown here:

    static uint8_t static_passkey[] = STATIC_PASSKEY;
    static ble_opt_t    m_static_pin_option;
    ...

    m_static_pin_option.gap_opt.passkey.p_passkey = static_passkey;
    err_code = sd_ble_opt_set(BLE_GAP_OPT_PASSKEY, &m_static_pin_option);
    APP_ERROR_CHECK(err_code);

I want to set a general static passkey to be used for maintenance people, such as an admin bonding passkey, and other passkey for the final user ( could be randomly generated or static) .

Is there a way to implement this? Can I change the static_passkey for an array of passkeys?

thanks in advance.

Parents
  • Hi,

    You cannot configure more than one passkey at a time, but you can call sd_ble_opt_set() with a new pass key. So you would have to keep the array in your application, and handle swapping between the keys yourself.

    Note that because of the way the passkey is exchanged in BLE using static passkey is not secure, as it can be brute forced quite fast (particularly with LE Secure Connections).

  • Thanks for the reply.

    Can you suggest me how to implement the handle swapping in the application?

    I though setting a new passkey using the sd_ble_opt_set() after the first attempt be fail, but to do this I wish the continue on the connection and retry without the peer be informed of that. But I just found after the miss value on the passkey the SoftDevices disconnects and return BLE_GAP_EVT_AUTH_STATUS not giving me the chance to authenticate manually before it disconnects.

    Any idea how to solve this case will be much appreciated!

  • Hi,

    pedrovfr_LNNano said:
    Can you suggest me how to implement the handle swapping in the application?

    You just call sd_ble_opt_set() again when you want to change the key. When that is will be application dependent so that is up to you.

    pedrovfr_LNNano said:
    I though setting a new passkey using the sd_ble_opt_set() after the first attempt be fail, but to do this I wish the continue on the connection and retry without the peer be informed of that. But I just found after the miss value on the passkey the SoftDevices disconnects and return BLE_GAP_EVT_AUTH_STATUS not giving me the chance to authenticate manually before it disconnects.

    The SDK examples and peer manager module will disconnect on failure by calling pm_handler_disconnect_on_sec_failure(). But this is really up to you, if you do not want to disconnect on failure do not call this in your peer manager event handler, and instead handle the PM_EVT_CONN_SEC_FAILED event in a different way that fits your application.

  • Hello, thanks for the reply.

    The SDK examples and peer manager module will disconnect on failure by calling pm_handler_disconnect_on_sec_failure(). But this is really up to you, if you do not want to disconnect on failure do not call this in your peer manager event handler, and instead handle the PM_EVT_CONN_SEC_FAILED event in a different way that fits your application.

    I have done that, but it still disconnects:

    /**@brief Function for handling Peer Manager events.
     *
     * @param[in] p_evt  Peer Manager event.
     */
    static void pm_evt_handler(pm_evt_t const * p_evt)
    {
        ret_code_t err_code;
        pm_handler_on_pm_evt(p_evt);
        //pm_handler_disconnect_on_sec_failure(p_evt);
        pm_handler_flash_clean(p_evt);
    
        switch (p_evt->evt_id)
        {
            case PM_EVT_PEER_DELETE_SUCCEEDED:
                if (!delete_bonds_pending() && !delete_all_pending)
                {
                    // No more peers are flagged for deletion and we are not going to delete all peers.
                    advertising_start(false);
                }
                break;
            case  PM_EVT_CONN_SEC_FAILED:
    
                printf("FAILED BONDING");
                    m_static_pin_option.gap_opt.passkey.p_passkey = ADMIN_PASSKEY;
                    err_code = sd_ble_opt_set(BLE_GAP_OPT_PASSKEY, &m_static_pin_option);
                    APP_ERROR_CHECK(err_code);
               
                break;
    
            case PM_EVT_PEERS_DELETE_SUCCEEDED:
                delete_all_pending = false;
                advertising_start(false);
                break;
    
            case PM_EVT_PEER_DATA_UPDATE_SUCCEEDED:
                if (     p_evt->params.peer_data_update_succeeded.flash_changed
                     && (p_evt->params.peer_data_update_succeeded.data_id == PM_PEER_DATA_ID_BONDING))
                {
                    NRF_LOG_INFO("New Bond, add the peer to the whitelist if possible");
                    // Note: You should check on what kind of white list policy your application should use.
    
                    whitelist_set(PM_PEER_ID_LIST_SKIP_NO_ID_ADDR);
                }
                break;
                        /** @snippet [NFC Pairing Lib usage_1] */
              case PM_EVT_CONN_SEC_PARAMS_REQ:
              {
                  // Send event to the NFC BLE pairing library as it may dynamically alternate
                  // security parameters to achieve highest possible security level.
                  
       //           APP_ERROR_CHECK(nfc_ble_pair_on_pm_params_req(p_evt));
              } break;
              /** @snippet [NFC Pairing Lib usage_1] */
    
                
            default:
                break;
        }
    }
     

    Here the debug terminal during a bonding attempt:

    <debug> app: pm_whitelist_get returns 0 addr in whitelist and 0 irk whitelist
    <info> app: Fast advertising.
    <debug> nrf_sdh_ble: BLE event: 0x10.
    <info> app: Connected
    <debug> nrf_ble_gq: Registering connection handle: 0x0000
    <debug> nrf_sdh_ble: BLE event: 0x12.
    <debug> nrf_sdh_ble: BLE event: 0x12.
    <debug> nrf_sdh_ble: BLE event: 0x13.
    <info> peer_manager_handler: Connection security procedure started: role: Peripheral, conn_handle: 0, procedure: Bonding
    <debug> nrf_sdh_ble: BLE event: 0x15.
    <info> app: Passkey: 123456
    <debug> nrf_sdh_ble: BLE event: 0x12.
    FAILED BONDING<debug> nrf_sdh_ble: BLE event: 0x19.
    <info> peer_manager_handler: Connection security failed: role: Peripheral, conn_handle: 0x0, procedure: Bonding, error: 132
    <debug> nrf_sdh_ble: BLE event: 0x11.
    <info> app: Disconnected

    I am receiving the BLE event BLE_GAP_EVT_DISCONNECTED in the ble event handler:

    /**@brief Function for handling BLE events.
     *
     * @param[in]   p_ble_evt   Bluetooth stack event.
     * @param[in]   p_context   Unused.
     */
    static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context)
    {
        ret_code_t err_code = NRF_SUCCESS;
        pm_handler_secure_on_connection(p_ble_evt);
        /////PEDROMOD
        pm_handler_secure_on_error(p_ble_evt);
        ////////////
        switch (p_ble_evt->header.evt_id)
        {
            case BLE_GAP_EVT_CONNECTED:
                NRF_LOG_INFO("Connected");
                err_code = bsp_indication_set(BSP_INDICATE_CONNECTED);
                APP_ERROR_CHECK(err_code);
                m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
                err_code = nrf_ble_bms_set_conn_handle(&m_bms, m_conn_handle);
                APP_ERROR_CHECK(err_code);
                err_code = nrf_ble_qwr_conn_handle_assign(&m_qwr, m_conn_handle);
                APP_ERROR_CHECK(err_code);
                err_code = nrf_ble_cgms_conn_handle_assign(&m_cgms, m_conn_handle);
                APP_ERROR_CHECK(err_code);
                break;
    
            case BLE_GAP_EVT_DISCONNECTED:
                NRF_LOG_INFO("Disconnected");
                if (delete_bonds_pending())
                {
                    // Advertising is started by PM_EVT_PEERS_DELETE_SUCCEEDED or PM_EVT_PEERS_DELETE_SUCCEEDED event.
                    delete_disconnected_bonds();
                }
                else
                {
                    advertising_start(false);
                }
                m_conn_handle = BLE_CONN_HANDLE_INVALID;
                //cgm_stop = true;
                break;
    
            case BLE_GAP_EVT_PHY_UPDATE_REQUEST:
            {
                NRF_LOG_DEBUG("PHY update request.");
                ble_gap_phys_t const phys =
                {
                    .rx_phys = BLE_GAP_PHY_AUTO,
                    .tx_phys = BLE_GAP_PHY_AUTO,
                };
                err_code = sd_ble_gap_phy_update(p_ble_evt->evt.gap_evt.conn_handle, &phys);
                APP_ERROR_CHECK(err_code);
            } break;
    
            case BLE_GATTC_EVT_TIMEOUT:
                // Disconnect on GATT Client timeout event.
                NRF_LOG_DEBUG("GATT Client Timeout.");
                err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gattc_evt.conn_handle,
                                                 BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
                APP_ERROR_CHECK(err_code);
                break;
    
            case BLE_GATTS_EVT_TIMEOUT:
                // Disconnect on GATT Server timeout event.
                NRF_LOG_DEBUG("GATT Server Timeout.");
                err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gatts_evt.conn_handle,
                                                 BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
                APP_ERROR_CHECK(err_code);
                break;
            case BLE_GAP_EVT_PASSKEY_DISPLAY:
            {
                char passkey[PASSKEY_LENGTH + 1];
                memcpy(passkey, p_ble_evt->evt.gap_evt.params.passkey_display.passkey, PASSKEY_LENGTH);
                passkey[PASSKEY_LENGTH] = 0;
    
                NRF_LOG_INFO("Passkey: %s", nrf_log_push(passkey));
            } break;
            case BLE_GAP_EVT_AUTH_KEY_REQUEST :
            {
    
                printf("KEY REQUEST");
            } break;
            default:
                // No implementation needed.
                break;
        }
    }
    

    Still not sure how the soft devices is handling the passkey, but once it doesn't match the connection is ended. Any ideias how to workaround this?

  • Hi,

    From the log we only see that you get BLE event 0x11, which is BLE_GAP_EVT_DISCONNECTED, but not why. Can you print the disconnect reason? It is not unlikely that the peer disconnects when the pairing fails. If so, you need to modify the behavior of the peer in order to prevent the disconnect (if possible). What is the peer device her?

  • Hello,

    My peer is an Android 10 motorola smartphone using the nRF Connect mobile app. I got the log there:

    D 09:39:52.754 gatt.setCharacteristicNotification(00002aa7-0000-1000-8000-00805f9b34fb, true)
    D 09:39:52.756 gatt.setCharacteristicNotification(00002a52-0000-1000-8000-00805f9b34fb, true)
    D 09:39:52.757 gatt.setCharacteristicNotification(00002aac-0000-1000-8000-00805f9b34fb, true)
    D 09:39:52.759 gatt.setCharacteristicNotification(00002a19-0000-1000-8000-00805f9b34fb, true)
    I 09:39:52.816 Connection parameters updated (interval: 48.75ms, latency: 0, timeout: 5000ms)
    D 09:39:54.983 [Broadcast] Action received: android.bluetooth.device.action.BOND_STATE_CHANGED, bond state changed to: BOND_BONDING (11)
    D 09:39:55.010 [Broadcast] Action received: android.bluetooth.device.action.PAIRING_REQUEST, pairing variant: PAIRING_VARIANT_CONSENT (3)
    D 09:39:57.268 [Broadcast] Action received: android.bluetooth.device.action.PAIRING_REQUEST, pairing variant: PAIRING_VARIANT_PIN (0)
    D 09:40:06.620 [Broadcast] Action received: android.bluetooth.device.action.BOND_STATE_CHANGED, bond state changed to: BOND_NONE (10)
    I 09:40:06.620 Bonding failed
    I 09:40:06.903 Connection parameters updated (interval: 498.75ms, latency: 0, timeout: 4000ms)
    D 09:40:10.056 [Callback] Connection state changed with status: 22 and new state: DISCONNECTED (0)
    E 09:40:10.056 Error 22 (0x16): GATT CONN TERMINATE LOCAL HOST
    I 09:40:10.056 Disconnected
    D 09:40:10.204 [Broadcast] Action received: android.bluetooth.device.action.ACL_DISCONNECTED

    Does it means the peer is disconnecting?

    From the log we only see that you get BLE event 0x11, which is BLE_GAP_EVT_DISCONNECTED, but not why. Can you print the disconnect reason?

    I am not sure how to print the reason on the application, could track only the event from the Soft Devices using a breakpoint:

    Also, is there a better way to see the reasons from the SD events?

  • Hi,

    pedrovfr_LNNano said:
    Does it means the peer is disconnecting?

    Yes, you see here that the local host terminated the connection (which refers to the Android device in the Android log). On the nRF side I expect you see that the remote user terminated the connection. I do not believe you can do anything about this on Android (or iOS) devices.

    pedrovfr_LNNano said:
    Also, is there a better way to see the reasons from the SD events?

    Yes. When  you get the BLE_GAP_EVT_DISCONNECTED you can check the reason and print it for instance like this:

                NRF_LOG_INFO("Disconnected, reason %d.",
                              p_ble_evt->evt.gap_evt.params.disconnected.reason);

    The number get here represents a standard Bluetooth status code.

Reply
  • Hi,

    pedrovfr_LNNano said:
    Does it means the peer is disconnecting?

    Yes, you see here that the local host terminated the connection (which refers to the Android device in the Android log). On the nRF side I expect you see that the remote user terminated the connection. I do not believe you can do anything about this on Android (or iOS) devices.

    pedrovfr_LNNano said:
    Also, is there a better way to see the reasons from the SD events?

    Yes. When  you get the BLE_GAP_EVT_DISCONNECTED you can check the reason and print it for instance like this:

                NRF_LOG_INFO("Disconnected, reason %d.",
                              p_ble_evt->evt.gap_evt.params.disconnected.reason);

    The number get here represents a standard Bluetooth status code.

Children
Related