Cant pair bluetooth peripheral with Secure connection and static passkey (Connect SDK)

I want to use the BLE Secure connection feature with the following options

CONFIG_BT_SMP_SC_PAIR_ONLY=y
CONFIG_BT_SMP_SC_ONLY=y
CONFIG_BT_SMP_APP_PAIRING_ACCEPT=y
CONFIG_BT_SMP_DISABLE_LEGACY_JW_PASSKEY=y
CONFIG_BT_FIXED_PASSKEY=y

I know that using a fixed passkey is discouraged but my peripheral will not have a display and the pairing should be done automatically. I have opted for creating a secret algorithm that generates the passkey dynamically with bt_passkey_set. The Central also knows the algorithm and can figure automatically which passkey should be sent.

Before I continue with the idea of generating dynamically the passkey with bt_passkey_set. I decided to enter a constant passkey at the beginning.

bt_passkey_set(123456);

Unfortunately when I try to pair with for example an Android Device I dont get the passkey dialog and the connection has a timeout and closes. If I do not use a static passkey with CONFIG_BT_FIXED_PASSKEY=y I get a random passkey, the BLE central gets the dialog to enter the passkey and zephyr calls the display passkey_display callback.

To Reproduce

Use any example that has a BLE peripheral and add the following Kconfig options

CONFIG_BT_SMP_SC_PAIR_ONLY=y
CONFIG_BT_SMP_SC_ONLY=y
CONFIG_BT_SMP_APP_PAIRING_ACCEPT=y
CONFIG_BT_SMP_DISABLE_LEGACY_JW_PASSKEY=y
CONFIG_BT_FIXED_PASSKEY=y

As callbacks use the following for authentication

static void auth_passkey_display(bt_conn *conn, unsigned int passkey)
{
	char addr[BT_ADDR_LE_STR_LEN];

	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));

	printk("Passkey for %s: %06u\n", addr, passkey);
}

static void auth_cancel(bt_conn *conn)
{
	char addr[BT_ADDR_LE_STR_LEN];

	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));

	printk("Pairing cancelled: %s\n", addr);
}

static void pairing_confirm(bt_conn *conn)
{
    bt_conn_auth_passkey_confirm(conn);
}

static bt_conn_auth_cb auth_cb_display = {
	.passkey_display = auth_passkey_display,
	.passkey_entry = nullptr,
    .cancel = auth_cancel,
    .pairing_confirm = pairing_confirm
};

In your bluetooth initialization call the following to register the callbacks and set the passkey:

bt_conn_auth_cb_register(&auth_cb_display);
bt_passkey_set(123456);


Expected behavior

Android Central device should get a passkey dialog to enter the passkey after pairing attempt with BLE Secure connection enabled on Zephyr.

If anyone could point me out what I am doing wrong API-wise or Kconfig-wise would be really helpful-
Parents
  • You say that you are aware that using a static passkey is discouraged, but did you know that the protocol is kind of broken when static passkeys are used? Static passkeys can be brute forced in on average 10 attempts. See insinuator.net/.../ for more info.

    Since you don't follow the standard if static passkeys are used, I suggest you to switch to Just Works method. You can add some other kind of security if you like on top of GATT, which will be more secure. The absolutely simplest would be to write your "dynamically generated secret" to some characteristic.

Reply
  • You say that you are aware that using a static passkey is discouraged, but did you know that the protocol is kind of broken when static passkeys are used? Static passkeys can be brute forced in on average 10 attempts. See insinuator.net/.../ for more info.

    Since you don't follow the standard if static passkeys are used, I suggest you to switch to Just Works method. You can add some other kind of security if you like on top of GATT, which will be more secure. The absolutely simplest would be to write your "dynamically generated secret" to some characteristic.

Children
  • Yes I saw that article and that is the reason I thought of generating a passkey that only the central and peripheral know how to generate depending on external inputs which are not static.

    The other route as you say instead I use another characteristic that allows me to unblock the functionality of the device. E.g. Peripheral generates a random value, central must write a secret passkey dependent on this value (e.g. encryption with AES256 with a secret salt), and as Peripheral knows this salt it can check if the central encrypted the value correctly.

  • Yes that is a simple way to solve the security problem unwanted access to the peripheral, but will not prevent for example subsequent passive eavesdropping.

    If you actually have a way to generate a random passkey, derived from external inputs, then security wise that will work just as good as an ordinary random passkey. The important thing here is that the 6 digit passkey is never reused.

Related