Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bluetooth: host: smp: Add bondable flag overlay per connection #59992

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions include/zephyr/bluetooth/conn.h
Original file line number Diff line number Diff line change
Expand Up @@ -1081,6 +1081,26 @@ void bt_conn_cb_register(struct bt_conn_cb *cb);
*/
void bt_set_bondable(bool enable);

/** @brief Set/clear the bonding flag for a given connection.
*
* Set/clear the Bonding flag in the Authentication Requirements of
* SMP Pairing Request/Response data for a given connection.
*
* The bonding flag for a given connection cannot be set/cleared if
* security procedures in the SMP module have already started.
* This function can be called only once per connection.
*
* If the bonding flag is not set/cleared for a given connection,
* the value will depend on global configuration which is set using
* bt_set_bondable.
* The default value of the global configuration is defined using
* CONFIG_BT_BONDABLE Kconfig option.
*
* @param conn Connection object.
* @param enable Value allowing/disallowing to be bondable.
*/
int bt_conn_set_bondable(struct bt_conn *conn, bool enable);

/** @brief Allow/disallow remote LE SC OOB data to be used for pairing.
*
* Set/clear the OOB data flag for LE SC SMP Pairing Request/Response data.
Expand Down
7 changes: 7 additions & 0 deletions subsys/bluetooth/host/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -600,6 +600,13 @@ config BT_BONDING_REQUIRED
set the bondable flag in their pairing request. Any other kind of
requests will be rejected.

config BT_BONDABLE_PER_CONNECTION
bool "Set/clear the bonding flag per-connection [EXPERIMENTAL]"
select EXPERIMENTAL
help
Enable support for the bt_conn_set_bondable API function that is
used to set/clear the bonding flag on a per-connection basis.

config BT_STORE_DEBUG_KEYS
bool "Store Debug Mode bonds"
help
Expand Down
38 changes: 36 additions & 2 deletions subsys/bluetooth/host/smp.c
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,9 @@ struct bt_smp {

/* Used Bluetooth authentication callbacks. */
atomic_ptr_t auth_cb;

/* Bondable flag */
atomic_t bondable;
};

static unsigned int fixed_passkey = BT_PASSKEY_INVALID;
Expand Down Expand Up @@ -288,6 +291,11 @@ static K_SEM_DEFINE(sc_local_pkey_ready, 0, 1);
*/
#define BT_SMP_AUTH_CB_UNINITIALIZED ((atomic_ptr_val_t)bt_smp_pool)

/* Value used to mark that per-connection bondable flag is not initialized.
* Value false/true represent if flag is cleared or set and cannot be used for that purpose.
*/
#define BT_SMP_BONDABLE_UNINITIALIZED ((atomic_val_t)-1)

static bool le_sc_supported(void)
{
/*
Expand All @@ -310,6 +318,13 @@ static const struct bt_conn_auth_cb *latch_auth_cb(struct bt_smp *smp)
return atomic_ptr_get(&smp->auth_cb);
}

static bool latch_bondable(struct bt_smp *smp)
{
atomic_cas(&smp->bondable, BT_SMP_BONDABLE_UNINITIALIZED, (atomic_val_t)bondable);

return atomic_get(&smp->bondable);
}

static uint8_t get_io_capa(struct bt_smp *smp)
{
const struct bt_conn_auth_cb *smp_auth_cb = latch_auth_cb(smp);
Expand Down Expand Up @@ -2592,7 +2607,7 @@ static uint8_t get_auth(struct bt_smp *smp, uint8_t auth)
auth |= BT_SMP_AUTH_MITM;
}

if (bondable) {
if (latch_bondable(smp)) {
auth |= BT_SMP_AUTH_BONDING;
} else {
auth &= ~BT_SMP_AUTH_BONDING;
Expand Down Expand Up @@ -3977,7 +3992,7 @@ static uint8_t smp_security_request(struct bt_smp *smp, struct net_buf *buf)
}

if (IS_ENABLED(CONFIG_BT_BONDING_REQUIRED) &&
!(bondable && (auth & BT_SMP_AUTH_BONDING))) {
!(latch_bondable(smp) && (auth & BT_SMP_AUTH_BONDING))) {
/* Reject security req if not both intend to bond */
LOG_DBG("Bonding required");
return BT_SMP_ERR_UNSPECIFIED;
Expand Down Expand Up @@ -4541,6 +4556,7 @@ static void bt_smp_connected(struct bt_l2cap_chan *chan)
smp_reset(smp);

atomic_ptr_set(&smp->auth_cb, BT_SMP_AUTH_CB_UNINITIALIZED);
atomic_set(&smp->bondable, BT_SMP_BONDABLE_UNINITIALIZED);
}

static void bt_smp_disconnected(struct bt_l2cap_chan *chan)
Expand Down Expand Up @@ -5291,6 +5307,24 @@ static inline int smp_self_test(void)
}
#endif

#if defined(CONFIG_BT_BONDABLE_PER_CONNECTION)
int bt_conn_set_bondable(struct bt_conn *conn, bool enable)
{
struct bt_smp *smp;

smp = smp_chan_get(conn);
if (!smp) {
return -EINVAL;
}

if (atomic_cas(&smp->bondable, BT_SMP_BONDABLE_UNINITIALIZED, (atomic_val_t)enable)) {
return 0;
} else {
return -EALREADY;
}
}
#endif

int bt_smp_auth_cb_overlay(struct bt_conn *conn, const struct bt_conn_auth_cb *cb)
{
struct bt_smp *smp;
Expand Down
1 change: 1 addition & 0 deletions tests/bsim/bluetooth/host/compile.sh
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ app=tests/bsim/bluetooth/host/privacy/legacy compile

app=tests/bsim/bluetooth/host/security/bond_overwrite_allowed compile
app=tests/bsim/bluetooth/host/security/bond_overwrite_denied compile
app=tests/bsim/bluetooth/host/security/bond_per_connection compile
app=tests/bsim/bluetooth/host/security/ccc_update compile
app=tests/bsim/bluetooth/host/security/ccc_update conf_file=prj_2.conf compile

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# SPDX-License-Identifier: Apache-2.0

cmake_minimum_required(VERSION 3.20.0)

find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(bsim_test_multi_id_bond_per_connection)

target_sources(app PRIVATE
src/bs_bt_utils.c
src/central.c
src/main.c
src/peripheral.c
)

zephyr_include_directories(
${BSIM_COMPONENTS_PATH}/libUtilv1/src/
${BSIM_COMPONENTS_PATH}/libPhyComv1/src/
)
17 changes: 17 additions & 0 deletions tests/bsim/bluetooth/host/security/bond_per_connection/prj.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
CONFIG_BT=y
CONFIG_BT_PERIPHERAL=y
CONFIG_BT_CENTRAL=y

CONFIG_BT_SMP=y

CONFIG_ASSERT=y
CONFIG_BT_TESTING=y
CONFIG_LOG=y

CONFIG_BT_ID_MAX=3
CONFIG_BT_MAX_PAIRED=2
CONFIG_BT_MAX_CONN=1

CONFIG_BT_PRIVACY=y

CONFIG_BT_BONDABLE_PER_CONNECTION=y
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
/*
* Copyright (c) 2023 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/

#include "bs_bt_utils.h"

BUILD_ASSERT(CONFIG_BT_MAX_PAIRED >= 2, "CONFIG_BT_MAX_PAIRED is too small.");
BUILD_ASSERT(CONFIG_BT_ID_MAX >= 3, "CONFIG_BT_ID_MAX is too small.");

#define BS_SECONDS(dur_sec) ((bs_time_t)dur_sec * 1000000)
#define TEST_TIMEOUT_SIMULATED BS_SECONDS(60)

void test_tick(bs_time_t HW_device_time)
{
bs_trace_debug_time(0, "Simulation ends now.\n");
if (bst_result != Passed) {
bst_result = Failed;
bs_trace_error("Test did not pass before simulation ended.\n");
}
}

void test_init(void)
{
bst_ticker_set_next_tick_absolute(TEST_TIMEOUT_SIMULATED);
bst_result = In_progress;
}

DEFINE_FLAG(flag_is_connected);
struct bt_conn *g_conn;
DEFINE_FLAG(bondable);
DEFINE_FLAG(call_bt_conn_set_bondable);

void wait_connected(void)
{
WAIT_FOR_FLAG(flag_is_connected);
}

void wait_disconnected(void)
{
WAIT_FOR_FLAG_UNSET(flag_is_connected);
}

static void disconnected(struct bt_conn *conn, uint8_t reason)
{
UNSET_FLAG(flag_is_connected);
}

BUILD_ASSERT(CONFIG_BT_MAX_CONN == 1, "This test assumes a single link.");
static void connected(struct bt_conn *conn, uint8_t err)
{
ASSERT((!g_conn || (conn == g_conn)), "Unexpected new connection.");

if (!g_conn) {
g_conn = bt_conn_ref(conn);
}

if (err != 0) {
clear_g_conn();
return;
}

SET_FLAG(flag_is_connected);

if (GET_FLAG(call_bt_conn_set_bondable) && bt_conn_set_bondable(conn, GET_FLAG(bondable))) {
ASSERT(0, "Fail during setting bondable flag for given connection.");
}
}

BT_CONN_CB_DEFINE(conn_callbacks) = {
.connected = connected,
.disconnected = disconnected,
};

void clear_g_conn(void)
{
struct bt_conn *conn;

conn = g_conn;
g_conn = NULL;
ASSERT(conn, "Test error: No g_conn!\n");
bt_conn_unref(conn);
}

/* The following flags are raised by events and lowered by test code. */
DEFINE_FLAG(flag_pairing_complete);
DEFINE_FLAG(flag_bonded);
DEFINE_FLAG(flag_not_bonded);

static void pairing_complete(struct bt_conn *conn, bool bonded)
{
SET_FLAG(flag_pairing_complete);

if (bonded) {
SET_FLAG(flag_bonded);
} else {
SET_FLAG(flag_not_bonded);
}
}

static struct bt_conn_auth_info_cb bt_conn_auth_info_cb = {
.pairing_complete = pairing_complete,
};

void bs_bt_utils_setup(void)
{
int err;

err = bt_enable(NULL);
ASSERT(!err, "bt_enable failed.\n");
err = bt_conn_auth_info_cb_register(&bt_conn_auth_info_cb);
ASSERT(!err, "bt_conn_auth_info_cb_register failed.\n");
}

static void scan_connect_to_first_result__device_found(const bt_addr_le_t *addr, int8_t rssi,
uint8_t type, struct net_buf_simple *ad)
{
char addr_str[BT_ADDR_LE_STR_LEN];
int err;

if (g_conn != NULL) {
return;
}

/* We're only interested in connectable events */
if (type != BT_HCI_ADV_IND && type != BT_HCI_ADV_DIRECT_IND) {
FAIL("Unexpected advertisement type.");
}

bt_addr_le_to_str(addr, addr_str, sizeof(addr_str));
printk("Got scan result, connecting.. dst %s, RSSI %d\n", addr_str, rssi);

err = bt_le_scan_stop();
ASSERT(!err, "Err bt_le_scan_stop %d", err);

err = bt_conn_le_create(addr, BT_CONN_LE_CREATE_CONN, BT_LE_CONN_PARAM_DEFAULT, &g_conn);
ASSERT(!err, "Err bt_conn_le_create %d", err);
}

void scan_connect_to_first_result(void)
{
int err;

err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, scan_connect_to_first_result__device_found);
ASSERT(!err, "Err bt_le_scan_start %d", err);
}

void disconnect(void)
{
int err;

err = bt_conn_disconnect(g_conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN);
ASSERT(!err, "Err bt_conn_disconnect %d", err);
}

void unpair(int id)
{
int err;

err = bt_unpair(id, BT_ADDR_LE_ANY);
ASSERT(!err, "Err bt_unpair %d", err);
}

void set_security(bt_security_t sec)
{
int err;

err = bt_conn_set_security(g_conn, sec);
ASSERT(!err, "Err bt_conn_set_security %d", err);
}

void advertise_connectable(int id, bt_addr_le_t *directed_dst)
{
int err;
struct bt_le_adv_param param = {};

param.id = id;
param.interval_min = 0x0020;
param.interval_max = 0x4000;
param.options |= BT_LE_ADV_OPT_ONE_TIME;
param.options |= BT_LE_ADV_OPT_CONNECTABLE;

if (directed_dst) {
param.options |= BT_LE_ADV_OPT_DIR_ADDR_RPA;
param.peer = directed_dst;
}

err = bt_le_adv_start(&param, NULL, 0, NULL, 0);
ASSERT(err == 0, "Advertising failed to start (err %d)\n", err);
}

void set_bondable(bool enable)
{
if (enable) {
SET_FLAG(bondable);
} else {
UNSET_FLAG(bondable);
}
}

void enable_bt_conn_set_bondable(bool enable)
{
if (enable) {
SET_FLAG(call_bt_conn_set_bondable);
} else {
UNSET_FLAG(call_bt_conn_set_bondable);
}
}
Loading
Loading