diff --git a/tests/bsim/bluetooth/host/compile.sh b/tests/bsim/bluetooth/host/compile.sh index aa12780f2d83079..dd5ef23471887d9 100755 --- a/tests/bsim/bluetooth/host/compile.sh +++ b/tests/bsim/bluetooth/host/compile.sh @@ -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 diff --git a/tests/bsim/bluetooth/host/security/bond_per_connection/CMakeLists.txt b/tests/bsim/bluetooth/host/security/bond_per_connection/CMakeLists.txt new file mode 100644 index 000000000000000..2fa69bf185fffee --- /dev/null +++ b/tests/bsim/bluetooth/host/security/bond_per_connection/CMakeLists.txt @@ -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/ + ) diff --git a/tests/bsim/bluetooth/host/security/bond_per_connection/prj.conf b/tests/bsim/bluetooth/host/security/bond_per_connection/prj.conf new file mode 100644 index 000000000000000..afa7d44903925b0 --- /dev/null +++ b/tests/bsim/bluetooth/host/security/bond_per_connection/prj.conf @@ -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 diff --git a/tests/bsim/bluetooth/host/security/bond_per_connection/src/bs_bt_utils.c b/tests/bsim/bluetooth/host/security/bond_per_connection/src/bs_bt_utils.c new file mode 100644 index 000000000000000..1523e10d8a5a188 --- /dev/null +++ b/tests/bsim/bluetooth/host/security/bond_per_connection/src/bs_bt_utils.c @@ -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(¶m, 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); + } +} diff --git a/tests/bsim/bluetooth/host/security/bond_per_connection/src/bs_bt_utils.h b/tests/bsim/bluetooth/host/security/bond_per_connection/src/bs_bt_utils.h new file mode 100644 index 000000000000000..fc440c6bec86886 --- /dev/null +++ b/tests/bsim/bluetooth/host/security/bond_per_connection/src/bs_bt_utils.h @@ -0,0 +1,85 @@ +/** + * Common functions and helpers for BSIM GATT tests + * + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "bs_tracing.h" +#include "bs_types.h" +#include "bstests.h" +#include "time_machine.h" +#include "zephyr/sys/__assert.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +extern enum bst_result_t bst_result; + +#define DECLARE_FLAG(flag) extern atomic_t flag +#define DEFINE_FLAG(flag) atomic_t flag = (atomic_t) false +#define SET_FLAG(flag) (void)atomic_set(&flag, (atomic_t) true) +#define UNSET_FLAG(flag) (void)atomic_set(&flag, (atomic_t) false) +#define WAIT_FOR_FLAG(flag) \ + while (!(bool)atomic_get(&flag)) { \ + (void)k_sleep(K_MSEC(1)); \ + } +#define WAIT_FOR_FLAG_UNSET(flag) \ + while ((bool)atomic_get(&flag)) { \ + (void)k_sleep(K_MSEC(1)); \ + } +#define TAKE_FLAG(flag) \ + while (!(bool)atomic_cas(&flag, true, false)) { \ + (void)k_sleep(K_MSEC(1)); \ + } +#define GET_FLAG(flag) \ + (bool)atomic_get(&flag) + +#define ASSERT(expr, ...) \ + do { \ + if (!(expr)) { \ + FAIL(__VA_ARGS__); \ + } \ + } while (0) + +#define FAIL(...) \ + do { \ + bst_result = Failed; \ + bs_trace_error_time_line(__VA_ARGS__); \ + } while (0) + +#define PASS(...) \ + do { \ + bst_result = Passed; \ + bs_trace_info_time(1, __VA_ARGS__); \ + } while (0) + +void test_tick(bs_time_t HW_device_time); +void test_init(void); + +DECLARE_FLAG(flag_pairing_complete); +DECLARE_FLAG(flag_bonded); +DECLARE_FLAG(flag_not_bonded); + +extern struct bt_conn *g_conn; +void wait_connected(void); +void wait_disconnected(void); +void clear_g_conn(void); +void bs_bt_utils_setup(void); +void scan_connect_to_first_result(void); +void disconnect(void); +void unpair(int id); +void set_security(bt_security_t sec); +void advertise_connectable(int id, bt_addr_le_t *directed_dst); +void set_bondable(bool enable); +void enable_bt_conn_set_bondable(bool enable); diff --git a/tests/bsim/bluetooth/host/security/bond_per_connection/src/central.c b/tests/bsim/bluetooth/host/security/bond_per_connection/src/central.c new file mode 100644 index 000000000000000..f31261b68c09dd8 --- /dev/null +++ b/tests/bsim/bluetooth/host/security/bond_per_connection/src/central.c @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "bs_bt_utils.h" +#include "zephyr/bluetooth/addr.h" +#include "zephyr/bluetooth/conn.h" + +#include + +#include + +void central(void) +{ + bs_bt_utils_setup(); + + printk("== Bonding id a - global bondable mode ==\n"); + BUILD_ASSERT(CONFIG_BT_BONDABLE, "CONFIG_BT_BONDABLE must be enabled by default."); + enable_bt_conn_set_bondable(false); + scan_connect_to_first_result(); + wait_connected(); + set_security(BT_SECURITY_L2); + TAKE_FLAG(flag_pairing_complete); + TAKE_FLAG(flag_bonded); + disconnect(); + wait_disconnected(); + unpair(BT_ID_DEFAULT); + clear_g_conn(); + + printk("== Bonding id a - bond per-connection ==\n"); + enable_bt_conn_set_bondable(true); + set_bondable(true); + scan_connect_to_first_result(); + wait_connected(); + set_security(BT_SECURITY_L2); + TAKE_FLAG(flag_pairing_complete); + TAKE_FLAG(flag_bonded); + disconnect(); + wait_disconnected(); + clear_g_conn(); + + printk("== Bonding id b - bond per-connection ==\n"); + scan_connect_to_first_result(); + wait_connected(); + set_security(BT_SECURITY_L2); + TAKE_FLAG(flag_pairing_complete); + TAKE_FLAG(flag_not_bonded); + disconnect(); + wait_disconnected(); + clear_g_conn(); + + PASS("PASS\n"); +} diff --git a/tests/bsim/bluetooth/host/security/bond_per_connection/src/main.c b/tests/bsim/bluetooth/host/security/bond_per_connection/src/main.c new file mode 100644 index 000000000000000..7553f7b5f83665e --- /dev/null +++ b/tests/bsim/bluetooth/host/security/bond_per_connection/src/main.c @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "bs_bt_utils.h" +#include "bstests.h" + +void central(void); +void peripheral(void); + +static const struct bst_test_instance test_to_add[] = { + { + .test_id = "central", + .test_post_init_f = test_init, + .test_tick_f = test_tick, + .test_main_f = central, + }, + { + .test_id = "peripheral", + .test_post_init_f = test_init, + .test_tick_f = test_tick, + .test_main_f = peripheral, + }, + BSTEST_END_MARKER, +}; + +static struct bst_test_list *install(struct bst_test_list *tests) +{ + return bst_add_tests(tests, test_to_add); +}; + +bst_test_install_t test_installers[] = {install, NULL}; + +int main(void) +{ + bst_main(); + return 0; +} diff --git a/tests/bsim/bluetooth/host/security/bond_per_connection/src/peripheral.c b/tests/bsim/bluetooth/host/security/bond_per_connection/src/peripheral.c new file mode 100644 index 000000000000000..5e11d6ac712fd64 --- /dev/null +++ b/tests/bsim/bluetooth/host/security/bond_per_connection/src/peripheral.c @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "bs_bt_utils.h" +#include "zephyr/bluetooth/addr.h" +#include "zephyr/bluetooth/bluetooth.h" +#include "zephyr/bluetooth/conn.h" +#include "zephyr/toolchain/gcc.h" + +#include +#include + +void peripheral(void) +{ + bs_bt_utils_setup(); + + int id_a; + int id_b; + + id_a = bt_id_create(NULL, NULL); + ASSERT(id_a >= 0, "bt_id_create id_a failed (err %d)\n", id_a); + + id_b = bt_id_create(NULL, NULL); + ASSERT(id_b >= 0, "bt_id_create id_b failed (err %d)\n", id_b); + + printk("== Bonding id a - global bondable mode ==\n"); + BUILD_ASSERT(CONFIG_BT_BONDABLE, "CONFIG_BT_BONDABLE must be enabled by default."); + enable_bt_conn_set_bondable(false); + advertise_connectable(id_a, NULL); + wait_connected(); + /* Central should bond here and trigger a disconnect. */ + wait_disconnected(); + unpair(id_a); + clear_g_conn(); + + printk("== Bonding id a - bond per-connection true ==\n"); + enable_bt_conn_set_bondable(true); + set_bondable(true); + advertise_connectable(id_a, NULL); + wait_connected(); + /* Central should bond here and trigger a disconnect. */ + wait_disconnected(); + clear_g_conn(); + + printk("== Bonding id b - bond per-connection false ==\n"); + set_bondable(false); + advertise_connectable(id_b, NULL); + wait_connected(); + /* Central should pair without bond here and trigger a disconnect. */ + wait_disconnected(); + + PASS("PASS\n"); +} diff --git a/tests/bsim/bluetooth/host/security/bond_per_connection/test_scripts/_compile.sh b/tests/bsim/bluetooth/host/security/bond_per_connection/test_scripts/_compile.sh new file mode 100755 index 000000000000000..f03de68b83f362e --- /dev/null +++ b/tests/bsim/bluetooth/host/security/bond_per_connection/test_scripts/_compile.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +# Copyright 2023 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +set -eu +bash_source_dir="$(realpath "$(dirname "${BASH_SOURCE[0]}")")" + +# Read variable definitions output by _env.sh +source "${bash_source_dir}/_env.sh" + +: "${BSIM_COMPONENTS_PATH:?BSIM_COMPONENTS_PATH must be defined}" +: "${ZEPHYR_BASE:?ZEPHYR_BASE must be defined}" + +WORK_DIR="${WORK_DIR:-${ZEPHYR_BASE}/bsim_out}" +BOARD="${BOARD:-nrf52_bsim}" +BOARD_ROOT="${BOARD_ROOT:-${ZEPHYR_BASE}}" +INCR_BUILD=1 +mkdir -p ${WORK_DIR} +source ${ZEPHYR_BASE}/tests/bsim/compile.source +app="tests/bsim/bluetooth/$test_name" compile diff --git a/tests/bsim/bluetooth/host/security/bond_per_connection/test_scripts/_env.sh b/tests/bsim/bluetooth/host/security/bond_per_connection/test_scripts/_env.sh new file mode 100755 index 000000000000000..173f0d2eedfcc0c --- /dev/null +++ b/tests/bsim/bluetooth/host/security/bond_per_connection/test_scripts/_env.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash +# Copyright 2023 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 +set -eu +bash_source_dir="$(realpath "$(dirname "${BASH_SOURCE[0]}")")" + +: "${BSIM_OUT_PATH:?BSIM_OUT_PATH must be defined}" + +test_name="security_bond_per_connection" +bsim_bin="${BSIM_OUT_PATH}/bin" +verbosity_level=2 +BOARD="${BOARD:-nrf52_bsim}" +simulation_id="$test_name" +central_exe="${bsim_bin}/bs_${BOARD}_tests_bsim_bluetooth_host_${test_name}_prj_conf" +peripheral_exe="${central_exe}" + +function print_var { + # Print a shell-sourceable variable definition. + local var_name="$1" + local var_repr="${!var_name@Q}" + echo "$var_name=$var_repr" +} + +print_var test_name +print_var bsim_bin +print_var verbosity_level +print_var BOARD +print_var simulation_id +print_var central_exe +print_var peripheral_exe diff --git a/tests/bsim/bluetooth/host/security/bond_per_connection/test_scripts/run_test.sh b/tests/bsim/bluetooth/host/security/bond_per_connection/test_scripts/run_test.sh new file mode 100755 index 000000000000000..351f44523cc1f2b --- /dev/null +++ b/tests/bsim/bluetooth/host/security/bond_per_connection/test_scripts/run_test.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +# Copyright 2023 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +set -eu +bash_source_dir="$(realpath "$(dirname "${BASH_SOURCE[0]}")")" + +# Read variable definitions output by _env.sh +source "${bash_source_dir}/_env.sh" +source ${ZEPHYR_BASE}/tests/bsim/sh_common.source + +EXECUTE_TIMEOUT=30 + +cd ${BSIM_OUT_PATH}/bin + +Execute "$central_exe" \ + -v=${verbosity_level} -s=${simulation_id} -d=0 -testid=central -RealEncryption=1 + +Execute "$peripheral_exe" \ + -v=${verbosity_level} -s=${simulation_id} -d=1 -testid=peripheral -RealEncryption=1 + +Execute ./bs_2G4_phy_v1 -v=${verbosity_level} -s=${simulation_id} \ + -D=2 -sim_length=60e6 $@ + +wait_for_background_jobs