Search code examples
androidiosbluetooth-lowenergygatt

Is there a way to pair without bonding using BLE GATT communication on Android/iOS?


Nice to meet you.

I am currently trying to connect to a BLE device that will be paired with JustWorks.

The target BLE device does not have a monitor or keyboard to confirm or enter the PIN, so the only way to encrypt between GATT communications is to use JustWorks.

At this time, a feature of this BLE device is that it discards the key exchanged in one connection without saving (bonding). The reason for this is that this BLE device cannot store the key permanently due to insufficient memory. The designer probably thought that it would be best to discard the key since many Android/iOS devices will be requesting pairing.

For this reason, I would like to encrypt (pairing) with JusWorks on the app side as well, but not save (bonding) the key. Is it possible to process this at the code level on the app side as well?

For now, my Android code implements the following coding.

GattManager.java

// GATT communication start processing
public void startConnection(GattPayload gattPayload) {
    // (omission)  Check Permissions
    String deviceAddress = gattPayload.getDeviceAddress();
       this.gattPayload = gattPayload;
    BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
    try {
       BluetoothDevice bluetoothDevice = bluetoothAdapter.getRemoteDevice(gattPayload.getDeviceAddress());
       bluetoothGatt = bluetoothDevice.connectGatt(context, false, gattCallback);
       registerPairingResultReceiver(context);
    } catch (IllegalArgumentException exception) {
       //(omission)
    }
}


// GATT communication callback
BluetoothGattCallback gattCallback = new BluetoothGattCallback() {

    @Override
    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
       super.onConnectionStateChange(gatt, status, newState);
       // (omission) Check Permissions

       if (newState == BluetoothProfile.STATE_CONNECTED) {
          BluetoothDevice device = gatt.getDevice();
          boolean result = device.createBond();
          if(!result){
             //Paired or error
             gatt.discoverServices();
          }

       } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
          disconnect();
       }
    }

    // (omission)
};

PairingResultReceiver.java

public class PairingResultReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)) {
            BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
            int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.ERROR);

            switch (state) {
                case BluetoothDevice.BOND_BONDED:
                    // Processing when pairing is successful
                    MyApplication.gattManager.serviceDiscover();
                    break;
                case BluetoothDevice.BOND_NONE:
                    // Processing when pairing fails or is canceled
                    break;
                case BluetoothDevice.BOND_BONDING:
                    // Processing during pairing process
                    break;
            }
        }
    }
}

I'll briefly explain the process. First, I connect with the connectGatt() method, and then call the createBond() method after the connection is successfully completed. The broadcast receiver then receives information that the user has allowed pairing, and moves on to the next GATT process. If the user rejects pairing, the process moves on to disconnecting. Although the development language and method of receiving information about whether or not pairing is possible on iOS are different, I would like to have a similar specification.

After this createBond() is processed, a local notification saying "Do you want to pair with this device? Yes / No" is displayed on the Android device screen. If the user (me) presses YES, pairing is completed, and subsequent processes went smoothly without any problems. I then confirmed the bonding by displaying the pairing list from the Android device's settings app, and saw that the name of the target device was displayed. This means that pairing and bonding are completed with just this code.

My request is to only pair without bonding in JustWorks. The reason is that there is a possibility that hundreds of BLE devices will be paired with this product. If hundreds of devices are paired, it would obviously be troublesome for the user to manually delete and manage the bonding information each time. However, if only the createBond() method is executed, pairing and bonding are processed automatically, and I do not know how to avoid bonding and only pairing.

Based on the above, I would like to ask a question.

[Q1] Does Android (or iOS) not provide a way to pair without bonding?

[Q2] If not, why?

[Q3] Please tell me the recommended design for secure GATT communication using encryption for this requirement.

I cannot find any related content on this, so does anyone know? Also, if there are any articles that answer this question, I would appreciate it if you could let me know.

If the content of the question does not fit the category, could you please introduce me to a recommended contact point?


Solution

  • If pairing causes so much issues for you, have you considered to communicate unencrypted instead? Note that you don't have to pair in order to communicate over BLE. Just Works isn't a strong method anyway since it is not secure against MITM-attacks. If you really need encryption, you can add your own security layer in the application using Diffie-Hellman as key exchange to get the same security level.

    If you still want to use standard BLE pairing, then the iOS and Android libraries offer basically almost zero APIs to configure the pairing and security, so you will have to do the configuration on the other side.

    The BLE standard has the feature that you can set the "bonding flag" of the Pairing Response packet in the Security Manager Protocol to 0. In this case, no bonding should be made but only pairing, so hence no keys shall be stored. If you do this, you should also set all the key bits to zeros (IRK, CSRK, LTK) in the same packet. If Android and iOS follows the BLE spec, no device entry should appear in the bond list if you do this. Note: make sure you enable LE Secure Connections since the Legacy Just Works pairing can be easily decrypted by a BLE sniffer.