diff --git a/doc/releases/migration-guide-4.3.rst b/doc/releases/migration-guide-4.3.rst index 1af99949b69ef..1357953f56199 100644 --- a/doc/releases/migration-guide-4.3.rst +++ b/doc/releases/migration-guide-4.3.rst @@ -215,6 +215,15 @@ Bluetooth Mesh been removed. The selection of the PSA Crypto provider is now automatically controlled by Kconfig :kconfig:option:`CONFIG_PSA_CRYPTO`. +Bluetooth Host +============== + +* :kconfig:option:`CONFIG_BT_FIXED_PASSKEY` has been deprecated. Instead, the application can + provide passkeys for pairing using the :c:member:`bt_conn_auth_cb.app_passkey` callback, which is + available when :kconfig:option:`CONFIG_BT_APP_PASSKEY` is enabled. The application can return the + passkey for pairing, or :c:macro:`BT_PASSKEY_RAND` for the Host to generate a random passkey + instead. + Ethernet ======== diff --git a/include/zephyr/bluetooth/conn.h b/include/zephyr/bluetooth/conn.h index 67c3db64e92a1..881d0894ee118 100644 --- a/include/zephyr/bluetooth/conn.h +++ b/include/zephyr/bluetooth/conn.h @@ -2380,8 +2380,8 @@ int bt_le_oob_get_sc_data(struct bt_conn *conn, const struct bt_le_oob_sc_data **oobd_remote); /** - * Special passkey value that can be used to disable a previously - * set fixed passkey. + * DEPRECATED - use @ref BT_PASSKEY_RAND instead. Special passkey value that can be used to disable + * a previously set fixed passkey. */ #define BT_PASSKEY_INVALID 0xffffffff @@ -2393,12 +2393,15 @@ int bt_le_oob_get_sc_data(struct bt_conn *conn, * Sets a fixed passkey to be used for pairing. If set, the * pairing_confirm() callback will be called for all incoming pairings. * + * @deprecated Use @ref BT_PASSKEY_RAND and the app_passkey callback from @ref bt_conn_auth_cb + * instead. + * * @param passkey A valid passkey (0 - 999999) or BT_PASSKEY_INVALID * to disable a previously set fixed passkey. * * @return 0 on success or a negative error code on failure. */ -int bt_passkey_set(unsigned int passkey); +__deprecated int bt_passkey_set(unsigned int passkey); /** Info Structure for OOB pairing */ struct bt_conn_oob_info { @@ -2464,6 +2467,13 @@ struct bt_conn_pairing_feat { }; #endif /* CONFIG_BT_SMP_APP_PAIRING_ACCEPT */ +/** + * Special passkey value that can be used to generate a random passkey when using the + * app_passkey callback from @ref bt_conn_auth_cb. + * + */ +#define BT_PASSKEY_RAND 0xffffffff + /** Authenticated pairing callback structure */ struct bt_conn_auth_cb { #if defined(CONFIG_BT_SMP_APP_PAIRING_ACCEPT) @@ -2663,6 +2673,30 @@ struct bt_conn_auth_cb { */ void (*pincode_entry)(struct bt_conn *conn, bool highsec); #endif + +#if defined(CONFIG_BT_APP_PASSKEY) + /** @brief Allow the application to provide a passkey for pairing. + * + * If implemented, this callback allows the application to provide passkeys for pairing. + * The valid range of passkeys is 0 - 999999. The application shall return the passkey for + * pairing, or BT_PASSKEY_RAND to generate a random passkey. This callback is invoked only + * for the Passkey Entry method as defined in Core Specification Vol. 3, Part H. Which + * device in the pairing is showing the passkey depends on the IO capabilities of the + * device; see Table 2.8 of the Bluetooth Core Specification V6.0, Vol. 3, Part H for more + * details. For the purposes of this table, the device gains the "display" capability when + * this callback is non-NULL. This is irrespective of whether the callback returns a + * specified key or BT_PASSKEY_RAND. + * + * + * @note When using this callback, it is the responsibility of the application to use + * random and unique keys. + * + * @param conn Connection where pairing is currently active. + * @return Passkey for pairing, or BT_PASSKEY_RAND for the Host to generate a random + * passkey. + */ + uint32_t (*app_passkey)(struct bt_conn *conn); +#endif /* CONFIG_BT_APP_PASSKEY */ }; /** Authenticated pairing information callback structure */ diff --git a/subsys/bluetooth/host/Kconfig b/subsys/bluetooth/host/Kconfig index 0e85437f3def3..df800f6a2bfcd 100644 --- a/subsys/bluetooth/host/Kconfig +++ b/subsys/bluetooth/host/Kconfig @@ -725,12 +725,24 @@ config BT_SMP_USB_HCI_CTLR_WORKAROUND if the keys are distributed over an encrypted link. config BT_FIXED_PASSKEY - bool "Use a fixed passkey for pairing" + bool "Use a fixed passkey for pairing [DEPRECATED]" + select DEPRECATED help + This option is deprecated, use BT_APP_PASSKEY instead. With this option enabled, the application will be able to call the bt_passkey_set() API to set a fixed passkey. If set, the pairing_confirm() callback will be called for all incoming pairings. +config BT_APP_PASSKEY + bool "Allow the application to provide passkeys for pairing" + depends on !BT_FIXED_PASSKEY + help + With this option enabled, the application will be able to provide passkeys for pairing + using the app_passkey() callback. If the application does not provide a passkey, a + random passkey will be generated by the Host. + + WARNING: It is the responsibility of the application to use random and unique keys. + config BT_USE_DEBUG_KEYS bool "Security Manager Debug Mode" help diff --git a/subsys/bluetooth/host/shell/bt.c b/subsys/bluetooth/host/shell/bt.c index a09afe4e3e5cb..816b6549853b0 100644 --- a/subsys/bluetooth/host/shell/bt.c +++ b/subsys/bluetooth/host/shell/bt.c @@ -4421,6 +4421,15 @@ static void br_bond_deleted(const bt_addr_t *peer) } #endif /* CONFIG_BT_CLASSIC */ +#if defined(CONFIG_BT_APP_PASSKEY) +static uint32_t app_passkey = BT_PASSKEY_RAND; + +static uint32_t auth_app_passkey(struct bt_conn *conn) +{ + return app_passkey; +} +#endif /* CONFIG_BT_APP_PASSKEY */ + static struct bt_conn_auth_cb auth_cb_display = { .passkey_display = auth_passkey_display, #if defined(CONFIG_BT_PASSKEY_KEYPRESS) @@ -4437,6 +4446,9 @@ static struct bt_conn_auth_cb auth_cb_display = { #if defined(CONFIG_BT_SMP_APP_PAIRING_ACCEPT) .pairing_accept = pairing_accept, #endif +#if defined(CONFIG_BT_APP_PASSKEY) + .app_passkey = auth_app_passkey, +#endif }; static struct bt_conn_auth_cb auth_cb_display_yes_no = { @@ -4445,6 +4457,9 @@ static struct bt_conn_auth_cb auth_cb_display_yes_no = { .passkey_confirm = auth_passkey_confirm, #if defined(CONFIG_BT_CLASSIC) .pincode_entry = auth_pincode_entry, +#endif +#if defined(CONFIG_BT_APP_PASSKEY) + .app_passkey = auth_app_passkey, #endif .oob_data_request = NULL, .cancel = auth_cancel, @@ -4460,6 +4475,9 @@ static struct bt_conn_auth_cb auth_cb_input = { .passkey_confirm = NULL, #if defined(CONFIG_BT_CLASSIC) .pincode_entry = auth_pincode_entry, +#endif +#if defined(CONFIG_BT_APP_PASSKEY) + .app_passkey = auth_app_passkey, #endif .oob_data_request = NULL, .cancel = auth_cancel, @@ -4472,6 +4490,9 @@ static struct bt_conn_auth_cb auth_cb_input = { static struct bt_conn_auth_cb auth_cb_confirm = { #if defined(CONFIG_BT_CLASSIC) .pincode_entry = auth_pincode_entry, +#endif +#if defined(CONFIG_BT_APP_PASSKEY) + .app_passkey = auth_app_passkey, #endif .oob_data_request = NULL, .cancel = auth_cancel, @@ -4487,6 +4508,9 @@ static struct bt_conn_auth_cb auth_cb_all = { .passkey_confirm = auth_passkey_confirm, #if defined(CONFIG_BT_CLASSIC) .pincode_entry = auth_pincode_entry, +#endif +#if defined(CONFIG_BT_APP_PASSKEY) + .app_passkey = auth_app_passkey, #endif .oob_data_request = auth_pairing_oob_data_request, .cancel = auth_cancel, @@ -4703,16 +4727,15 @@ static int cmd_fal_connect(const struct shell *sh, size_t argc, char *argv[]) #endif /* CONFIG_BT_CENTRAL */ #endif /* defined(CONFIG_BT_FILTER_ACCEPT_LIST) */ -#if defined(CONFIG_BT_FIXED_PASSKEY) -static int cmd_fixed_passkey(const struct shell *sh, - size_t argc, char *argv[]) +#if defined(CONFIG_BT_APP_PASSKEY) +static int cmd_app_passkey(const struct shell *sh, + size_t argc, char *argv[]) { - unsigned int passkey; - int err; + uint32_t passkey; if (argc < 2) { - bt_passkey_set(BT_PASSKEY_INVALID); - shell_print(sh, "Fixed passkey cleared"); + app_passkey = BT_PASSKEY_RAND; + shell_print(sh, "App passkey cleared"); return 0; } @@ -4722,14 +4745,12 @@ static int cmd_fixed_passkey(const struct shell *sh, return -ENOEXEC; } - err = bt_passkey_set(passkey); - if (err) { - shell_print(sh, "Setting fixed passkey failed (err %d)", err); - } + app_passkey = passkey; + shell_print(sh, "App passkey set to %06u", passkey); - return err; + return 0; } -#endif +#endif /* CONFIG_BT_APP_PASSKEY */ static int cmd_auth_passkey(const struct shell *sh, size_t argc, char *argv[]) @@ -5340,10 +5361,10 @@ SHELL_STATIC_SUBCMD_SET_CREATE(bt_cmds, cmd_fal_connect, 2, 3), #endif /* CONFIG_BT_CENTRAL */ #endif /* defined(CONFIG_BT_FILTER_ACCEPT_LIST) */ -#if defined(CONFIG_BT_FIXED_PASSKEY) - SHELL_CMD_ARG(fixed-passkey, NULL, "[passkey]", cmd_fixed_passkey, +#if defined(CONFIG_BT_APP_PASSKEY) + SHELL_CMD_ARG(app-passkey, NULL, "[passkey]", cmd_app_passkey, 1, 1), -#endif +#endif /* CONFIG_BT_APP_PASSKEY */ #endif /* CONFIG_BT_SMP || CONFIG_BT_CLASSIC) */ #endif /* CONFIG_BT_CONN */ diff --git a/subsys/bluetooth/host/smp.c b/subsys/bluetooth/host/smp.c index 702e6fc0d215e..b935c61d2a36a 100644 --- a/subsys/bluetooth/host/smp.c +++ b/subsys/bluetooth/host/smp.c @@ -222,10 +222,10 @@ struct bt_smp { atomic_t bondable; }; -static unsigned int fixed_passkey = BT_PASSKEY_INVALID; +static unsigned int fixed_passkey = BT_PASSKEY_RAND; #define DISPLAY_FIXED(smp) (IS_ENABLED(CONFIG_BT_FIXED_PASSKEY) && \ - fixed_passkey != BT_PASSKEY_INVALID && \ + fixed_passkey != BT_PASSKEY_RAND && \ (smp)->method == PASSKEY_DISPLAY) #if !defined(CONFIG_BT_SMP_SC_PAIR_ONLY) @@ -363,9 +363,21 @@ static uint8_t get_io_capa(struct bt_smp *smp) return BT_SMP_IO_DISPLAY_YESNO; } +#if defined(CONFIG_BT_APP_PASSKEY) + /* Implementation of the app_passkey cb implies that the application can "know" the passkey + * without actually having a display, thus earning the "display" capability. + */ + if (smp_auth_cb->app_passkey) { + if (smp_auth_cb->passkey_entry) { + return BT_SMP_IO_KEYBOARD_DISPLAY; + } + + return BT_SMP_IO_DISPLAY_ONLY; + } +#endif /* CONFIG_BT_APP_PASSKEY */ + if (smp_auth_cb->passkey_entry) { - if (IS_ENABLED(CONFIG_BT_FIXED_PASSKEY) && - fixed_passkey != BT_PASSKEY_INVALID) { + if (IS_ENABLED(CONFIG_BT_FIXED_PASSKEY) && fixed_passkey != BT_PASSKEY_RAND) { return BT_SMP_IO_KEYBOARD_DISPLAY; } else { return BT_SMP_IO_KEYBOARD_ONLY; @@ -377,8 +389,7 @@ static uint8_t get_io_capa(struct bt_smp *smp) } no_callbacks: - if (IS_ENABLED(CONFIG_BT_FIXED_PASSKEY) && - fixed_passkey != BT_PASSKEY_INVALID) { + if (IS_ENABLED(CONFIG_BT_FIXED_PASSKEY) && fixed_passkey != BT_PASSKEY_RAND) { return BT_SMP_IO_DISPLAY_ONLY; } else { return BT_SMP_IO_NO_INPUT_OUTPUT; @@ -2475,7 +2486,6 @@ static uint8_t legacy_request_tk(struct bt_smp *smp) struct bt_conn *conn = smp->chan.chan.conn; const struct bt_conn_auth_cb *smp_auth_cb = latch_auth_cb(smp); struct bt_keys *keys; - uint32_t passkey; /* * Fail if we have keys that are stronger than keys that will be @@ -2503,11 +2513,25 @@ static uint8_t legacy_request_tk(struct bt_smp *smp) } break; - case PASSKEY_DISPLAY: - if (IS_ENABLED(CONFIG_BT_FIXED_PASSKEY) && - fixed_passkey != BT_PASSKEY_INVALID) { + case PASSKEY_DISPLAY: { + uint32_t passkey; + + if (IS_ENABLED(CONFIG_BT_FIXED_PASSKEY) && fixed_passkey != BT_PASSKEY_RAND) { passkey = fixed_passkey; - } else { +#if defined(CONFIG_BT_APP_PASSKEY) + } else if (smp_auth_cb && smp_auth_cb->app_passkey) { + passkey = smp_auth_cb->app_passkey(conn); + + if (passkey != BT_PASSKEY_RAND && passkey > 999999) { + LOG_WRN("App-provided passkey is out of valid range: %u", passkey); + return BT_SMP_ERR_UNSPECIFIED; + } +#endif /* CONFIG_BT_APP_PASSKEY */ + } else { + passkey = BT_PASSKEY_RAND; + } + + if (passkey == BT_PASSKEY_RAND) { if (bt_rand(&passkey, sizeof(passkey))) { return BT_SMP_ERR_UNSPECIFIED; } @@ -2527,6 +2551,7 @@ static uint8_t legacy_request_tk(struct bt_smp *smp) sys_put_le32(passkey, smp->tk); break; + } case PASSKEY_INPUT: atomic_set_bit(smp->flags, SMP_FLAG_USER); smp_auth_cb->passkey_entry(conn); @@ -4429,18 +4454,32 @@ __maybe_unused static uint8_t display_passkey(struct bt_smp *smp) { struct bt_conn *conn = smp->chan.chan.conn; const struct bt_conn_auth_cb *smp_auth_cb = latch_auth_cb(smp); + uint32_t passkey = BT_PASSKEY_RAND; - if (IS_ENABLED(CONFIG_BT_FIXED_PASSKEY) && - fixed_passkey != BT_PASSKEY_INVALID) { - smp->passkey = fixed_passkey; - } else { - if (bt_rand(&smp->passkey, sizeof(smp->passkey))) { + if (IS_ENABLED(CONFIG_BT_FIXED_PASSKEY) && fixed_passkey != BT_PASSKEY_RAND) { + passkey = fixed_passkey; + } + +#if defined(CONFIG_BT_APP_PASSKEY) + if (smp_auth_cb && smp_auth_cb->app_passkey) { + passkey = smp_auth_cb->app_passkey(conn); + + if (passkey != BT_PASSKEY_RAND && passkey > 999999) { + LOG_WRN("App-provided passkey is out of valid range: %u", passkey); + return BT_SMP_ERR_UNSPECIFIED; + } + } +#endif /* CONFIG_BT_APP_PASSKEY */ + + if (passkey == BT_PASSKEY_RAND) { + if (bt_rand(&passkey, sizeof(passkey))) { return BT_SMP_ERR_UNSPECIFIED; } - smp->passkey %= 1000000; + passkey %= 1000000; } + smp->passkey = passkey; smp->passkey_round = 0U; if (smp_auth_cb && smp_auth_cb->passkey_display) { @@ -6172,8 +6211,8 @@ int bt_smp_auth_pairing_confirm(struct bt_conn *conn) #if defined(CONFIG_BT_FIXED_PASSKEY) int bt_passkey_set(unsigned int passkey) { - if (passkey == BT_PASSKEY_INVALID) { - fixed_passkey = BT_PASSKEY_INVALID; + if (passkey == BT_PASSKEY_INVALID || passkey == BT_PASSKEY_RAND) { + fixed_passkey = BT_PASSKEY_RAND; return 0; } diff --git a/tests/bluetooth/shell/audio.conf b/tests/bluetooth/shell/audio.conf index f4143e31aac3e..dc940c0cc0b5e 100644 --- a/tests/bluetooth/shell/audio.conf +++ b/tests/bluetooth/shell/audio.conf @@ -22,7 +22,7 @@ CONFIG_BT_GATT_AUTO_DISCOVER_CCC=y CONFIG_BT_GATT_AUTO_UPDATE_MTU=y CONFIG_BT_L2CAP_ECRED=y CONFIG_BT_SIGNING=y -CONFIG_BT_FIXED_PASSKEY=y +CONFIG_BT_APP_PASSKEY=y CONFIG_BT_ATT_PREPARE_COUNT=5 CONFIG_BT_SHELL=y CONFIG_BT_DEVICE_NAME="audio test shell" diff --git a/tests/bluetooth/shell/log.conf b/tests/bluetooth/shell/log.conf index 746a3bbe57021..9068129f01aac 100644 --- a/tests/bluetooth/shell/log.conf +++ b/tests/bluetooth/shell/log.conf @@ -9,7 +9,7 @@ CONFIG_BT_PERIPHERAL=y CONFIG_BT_PRIVACY=y CONFIG_BT_SMP=y CONFIG_BT_SIGNING=y -CONFIG_BT_FIXED_PASSKEY=y +CONFIG_BT_APP_PASSKEY=y CONFIG_BT_ATT_PREPARE_COUNT=2 CONFIG_BT_GATT_CLIENT=y CONFIG_BT_L2CAP_DYNAMIC_CHANNEL=y diff --git a/tests/bluetooth/shell/prj.conf b/tests/bluetooth/shell/prj.conf index 85f9ae0072057..54c904e957a87 100644 --- a/tests/bluetooth/shell/prj.conf +++ b/tests/bluetooth/shell/prj.conf @@ -11,7 +11,7 @@ CONFIG_BT_PRIVACY=y CONFIG_BT_SMP=y CONFIG_BT_PASSKEY_KEYPRESS=y CONFIG_BT_SIGNING=y -CONFIG_BT_FIXED_PASSKEY=y +CONFIG_BT_APP_PASSKEY=y CONFIG_BT_ATT_PREPARE_COUNT=2 CONFIG_BT_GATT_CLIENT=y CONFIG_BT_L2CAP_DYNAMIC_CHANNEL=y