non-bondable pairing in nrf-connect sdk

Hello, 

I'm trying to configure the BLE stack as non-bondable pairing only so that it requires pairing by every time the smartphone tries to connect to the device (the bonding is not required). I changed the prj.config and the code, but it seems to work with iPhone but not with Android. 

My modifications 

In prj.config

CONFIG_BT_MAX_CONN=1
CONFIG_BT_MAX_PAIRED=1
CONFIG_BT_TEST_SECURITY_ENABLED=y
CONFIG_BT_TEST_AUTHEN=y
CONFIG_BT_SMP=y
CONFIG_BT_FIXED_PASSKEY=y
CONFIG_BLE_PASSKEY=123456
CONFIG_BT_BONDABLE=n

Code snapshot.

static void connected(struct bt_conn *conn, uint8_t err)
{
	char addr[BT_ADDR_LE_STR_LEN];

	if (err) {
		LOG_ERR("Connection failed (err %u)", err);
		return;
	}

	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
	LOG_INF("Connected %s", addr);
	ble.current_conn = bt_conn_ref(conn);
	state_set(STATE_BLE_CONNECTED);
}

static void disconnected(struct bt_conn *conn, uint8_t reason)
{
	char addr[BT_ADDR_LE_STR_LEN];

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

	LOG_INF("Disconnected: %s (reason %u)", addr, reason);
	bt_unpair(BT_ID_DEFAULT, NULL);

	if (ble.auth_conn) {
		bt_conn_unref(ble.auth_conn);
		ble.auth_conn = NULL;
	}
	
	if (ble.current_conn) {
		bt_conn_unref(ble.current_conn);
		ble.current_conn = NULL;
	}
	 state_set(STATE_BLE_DISCONNECTED);
	
}

static bool le_param_req(struct bt_conn *conn, struct bt_le_conn_param *param)
{
	//If acceptable params, return true, otherwise return false.
	return true; 
}

static void le_param_updated(struct bt_conn *conn, uint16_t interval, uint16_t latency, uint16_t timeout)
{
	struct bt_conn_info info; 
	char addr[BT_ADDR_LE_STR_LEN];
	
	if(bt_conn_get_info(conn, &info))
	{
		LOG_INF("Could not parse connection info\n");
	}
	else
	{
		bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
		
		// LOG_("Connection parameters updated!	\n\
		// Connected to: %s						\n\
		// New Connection Interval: %u				\n\
		// New Slave Latency: %u					\n\
		// New Connection Supervisory Timeout: %u	\n"
		// , addr, info.le.interval, info.le.latency, info.le.timeout);
	}
}


#ifdef CONFIG_BT_TEST_SECURITY_ENABLED
static void security_changed(struct bt_conn *conn, bt_security_t level,
			     enum bt_security_err err)
{
	char addr[BT_ADDR_LE_STR_LEN];

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

	if (!err) {
		LOG_INF("Security changed: %s level %u", addr, level);
	} else {
		LOG_WRN("Security failed: %s level %u err %d", addr,
			level, err);
	}
}
#endif

BT_CONN_CB_DEFINE(conn_callbacks) = {
	.connected          = connected,
	.disconnected       = disconnected,
	.le_param_req		= le_param_req,
	.le_param_updated	= le_param_updated,
#ifdef CONFIG_BT_TEST_SECURITY_ENABLED
	.security_changed = security_changed,
#endif
};

#if defined(CONFIG_BT_TEST_SECURITY_ENABLED)
static void auth_passkey_display(struct 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));

	LOG_INF("Passkey for %s: %06u", addr, passkey);
}

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

	ble.auth_conn = bt_conn_ref(conn);

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

	LOG_INF("Passkey for %s: %06u", addr, passkey);
	LOG_INF("Press Button 1 to confirm, Button 2 to reject.");
}


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

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

	LOG_INF("Pairing cancelled: %s", addr);
}

static void pairing_confirm(struct bt_conn *conn)
{
	bt_conn_auth_cancel(conn);
}

static void pairing_complete(struct bt_conn *conn, bool bonded)
{
	char addr[BT_ADDR_LE_STR_LEN];

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

	LOG_INF("Pairing completed: %s, bonded: %d", addr, bonded);
}


static void pairing_failed(struct bt_conn *conn, enum bt_security_err reason)
{
	char addr[BT_ADDR_LE_STR_LEN];

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

	LOG_INF("Pairing failed conn: %s, reason %d", addr, reason);
}


static struct bt_conn_auth_cb conn_auth_callbacks = {
	.passkey_display = NULL,
	.passkey_confirm = NULL,
	.cancel = auth_cancel,
};

static struct bt_conn_auth_info_cb conn_auth_info_callbacks = {
	.pairing_complete = pairing_complete,
	.pairing_failed = pairing_failed,
};
#else
static struct bt_conn_auth_cb conn_auth_callbacks;
static struct bt_conn_auth_info_cb conn_auth_info_callbacks;
#endif



#ifdef CONFIG_BT_TEST_SECURITY_ENABLED
static void num_comp_reply(bool accept)
{
	if (accept) {
		bt_conn_auth_passkey_confirm(ble.auth_conn);
		LOG_INF("Numeric Match, conn %p", (void *)ble.auth_conn);
	} else {
		bt_conn_auth_cancel(ble.auth_conn);
		LOG_INF("Numeric Reject, conn %p", (void *)ble.auth_conn);
	}

	bt_conn_unref(ble.auth_conn);
	ble.auth_conn = NULL;
}

#endif /* CONFIG_BT_TEST_SECURITY_ENABLED */


/**************************************************************************//**
 * @brief Initialize BLE.
 *****************************************************************************/
int ble_enable() {
	
	int blink_status = 0;
	int err = 0;

	if (IS_ENABLED(CONFIG_BT_TEST_SECURITY_ENABLED)) {
		err = bt_conn_auth_cb_register(&conn_auth_callbacks);
		if (err) {
			LOG_ERR("Failed to register authorization callbacks.");
			return;
		}

		err = bt_conn_auth_info_cb_register(&conn_auth_info_callbacks);
		if (err) {
			LOG_ERR("Failed to register authorization info callbacks.");
			return;
		}
	}

	bt_set_bondable(false);
	bt_passkey_set(CONFIG_BLE_PASSKEY);

	err = bt_enable(NULL);
	if (err) {
		error();
	}

	LOG_INF("Bluetooth initialized");

	k_sem_give(&ble_init_ok);

	if (IS_ENABLED(CONFIG_SETTINGS)) {
		settings_load();
	}

	err = bt_test_init(&test_cb);
	if (err) {
		LOG_ERR("Failed to initialize UART service (err: %d)", err);
		return;
	}

	err = bt_le_adv_start(BT_LE_ADV_CONN, ad, ARRAY_SIZE(ad), sd,
			      ARRAY_SIZE(sd));
	if (err) {
		LOG_ERR("Advertising failed to start (err %d)", err);
		return;
	}
	return 0;
}

Could you please tell me what else I need to modify or change to get it working on Android? 

Current behavior on Android phones.

My first connection works fine ( it requires pairing ) but the second connection doesn't require the pairing key so it fails since the hardware executes by every disconnect  (

bt_unpair) bc, I want the user enters Passkey by every time the connection is established. 

. It seems that the android phone thinks that it has the right authentication keys  (the second time) so it fails and deletes those keys after failing. Afterward, the third connection works fine and it requires again the pairing key. so result the one connection works second one does not and so on. 

Thanks in advance! 

Tools: 

SDK: 2.2
hardware: nrf53

  • the complete project will be difficult to upload here for the public, is there a way for private upload?

    but anyway you can test it here. pinCode: 682711

    test firmware.hex

  • Hi Mustafa, 
    I duplicated what you observed. With the provided firmware , the second connect was kept. And the phone didn't try to re-encrypt the link , even though the status was "bonded" . 
    What made the different was that the firmware you provided was with Legacy Pairing. When with Zephyr it was LE Secure Connection. 
    When I changed the nRF5 SDK example to do Legacy Pairing, I got the same result that 2nd connection was fine.

    But switched back to LE Secure Connection, we see the issue with 2nd connection again. 

    The main difference between LE Secure Connection and Legacy pairing is that with LE Secure Connection LTK is generated regardless if you do pairing or you do bonding. When with Legacy pairing if you have the bonding flag off , the LTK will not be generated and exchanged. 

    The challenge here is that legacy pairing is not secured, even if you use passkey. So in Zephyr as far as I know there isn't an option to disable LE Secure Connection.

    The issue here is the issue with Android that it doesn't take the No Bonding flag into account and still try to re-bond even though it was only pairing, not bonding. 

  • Thanks for your answer!. I'm just wondering about LTK, I thought the pairing process will generate STK even though it is Legacy pairing, and the bonding process requires Long term key, right?

  • Hi Mustafa, 
    Yes it's correct for legacy pairing. 
    For LESC, the LTK is generated regardless. There is no TK, STK in LESC , only LTK. When choosing to do pairing only (no bonding) the LTK should be discarded after connection is terminated. 

  • Hi again, 
    I found that you can force Legacy Pairing by these setting: 

    CONFIG_BT_CTLR_ECDH=n
    CONFIG_BT_TINYCRYPT_ECC=n

    But as I said earlier, pairing using Legacy Pairing + passkey is not secured. 
    Pairing with LESC + static passkey is also not secured :) 
Related