From c446c8267b6c2ea4c37d0fbe11b7994660ca4edb Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 30 Jul 2018 20:29:40 +0300 Subject: [PATCH] Bluetooth: Add support for fixed passkeys Add a new bt_passkey_set() API that can be used to set a fixed passkey to be used for pairing. The new API also requires a new Kconfig option to be enabled first (CONFIG_BT_FIXED_PASSKEY). Fixes #8350 Signed-off-by: Johan Hedberg --- include/bluetooth/conn.h | 22 ++++++ subsys/bluetooth/host/Kconfig | 7 ++ subsys/bluetooth/host/conn.c | 1 - subsys/bluetooth/host/smp.c | 127 ++++++++++++++++++++++------------ 4 files changed, 113 insertions(+), 44 deletions(-) diff --git a/include/bluetooth/conn.h b/include/bluetooth/conn.h index 2ab6ba97011..0327583d595 100644 --- a/include/bluetooth/conn.h +++ b/include/bluetooth/conn.h @@ -382,6 +382,28 @@ struct bt_conn_cb { */ void bt_conn_cb_register(struct bt_conn_cb *cb); +/** @def BT_PASSKEY_INVALID + * + * Special passkey value that can be used to disable a previously + * set fixed passkey. + */ +#define BT_PASSKEY_INVALID 0xffffffff + +/** @brief Set a fixed passkey to be used for pairing. + * + * This API is only available when the CONFIG_BT_FIXED_PASSKEY + * configuration option has been enabled. + * + * Sets a fixed passkey to be used for pairing. If set, the + * pairing_confim() callback will be called for all incoming pairings. + * + * @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); + /** Authenticated pairing callback structure */ struct bt_conn_auth_cb { /** @brief Display a passkey to the user. diff --git a/subsys/bluetooth/host/Kconfig b/subsys/bluetooth/host/Kconfig index 963811da1d4..c685bea8aa3 100644 --- a/subsys/bluetooth/host/Kconfig +++ b/subsys/bluetooth/host/Kconfig @@ -281,6 +281,13 @@ config BT_SMP_SC_ONLY Security Mode 1 Level 4 stands for authenticated LE Secure Connections pairing with encryption. Enabling this option disables legacy pairing. +config BT_FIXED_PASSKEY + bool "Use a fixed passkey for pairing" + help + 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_confim() callback will be called for all incoming pairings. + config BT_USE_DEBUG_KEYS bool "Enable Security Manager Debug Mode" depends on BT_TINYCRYPT_ECC diff --git a/subsys/bluetooth/host/conn.c b/subsys/bluetooth/host/conn.c index b49b2986e46..7286f932701 100644 --- a/subsys/bluetooth/host/conn.c +++ b/subsys/bluetooth/host/conn.c @@ -1967,7 +1967,6 @@ int bt_conn_auth_cb_register(const struct bt_conn_auth_cb *cb) return -EINVAL; } - bt_auth = cb; return 0; } diff --git a/subsys/bluetooth/host/smp.c b/subsys/bluetooth/host/smp.c index ac7d582975f..e7aaa903de1 100644 --- a/subsys/bluetooth/host/smp.c +++ b/subsys/bluetooth/host/smp.c @@ -174,6 +174,12 @@ struct bt_smp { struct k_delayed_work work; }; +static unsigned int fixed_passkey = BT_PASSKEY_INVALID; + +#define DISPLAY_FIXED(smp) (IS_ENABLED(CONFIG_BT_FIXED_PASSKEY) && \ + fixed_passkey != BT_PASSKEY_INVALID && \ + (smp)->method == PASSKEY_DISPLAY) + #if !defined(CONFIG_BT_SMP_SC_ONLY) /* based on table 2.8 Core Spec 2.3.5.1 Vol. 3 Part H */ static const u8_t gen_method_legacy[5 /* remote */][5 /* local */] = { @@ -244,7 +250,7 @@ static u8_t sc_public_key[64]; static u8_t get_io_capa(void) { if (!bt_auth) { - return BT_SMP_IO_NO_INPUT_OUTPUT; + goto no_callbacks; } /* Passkey Confirmation is valid only for LE SC */ @@ -260,14 +266,25 @@ static u8_t get_io_capa(void) } if (bt_auth->passkey_entry) { - return BT_SMP_IO_KEYBOARD_ONLY; + if (IS_ENABLED(CONFIG_BT_FIXED_PASSKEY) && + fixed_passkey != BT_PASSKEY_INVALID) { + return BT_SMP_IO_KEYBOARD_DISPLAY; + } else { + return BT_SMP_IO_KEYBOARD_ONLY; + } } if (bt_auth->passkey_display) { return BT_SMP_IO_DISPLAY_ONLY; } - return BT_SMP_IO_NO_INPUT_OUTPUT; +no_callbacks: + if (IS_ENABLED(CONFIG_BT_FIXED_PASSKEY) && + fixed_passkey != BT_PASSKEY_INVALID) { + return BT_SMP_IO_DISPLAY_ONLY; + } else { + return BT_SMP_IO_NO_INPUT_OUTPUT; + } } static u8_t get_pair_method(struct bt_smp *smp, u8_t remote_io) @@ -1916,16 +1933,23 @@ static u8_t legacy_request_tk(struct bt_smp *smp) switch (smp->method) { case PASSKEY_DISPLAY: - if (bt_rand(&passkey, sizeof(passkey))) { - return BT_SMP_ERR_UNSPECIFIED; + if (IS_ENABLED(CONFIG_BT_FIXED_PASSKEY) && + fixed_passkey != BT_PASSKEY_INVALID) { + passkey = fixed_passkey; + } else { + if (bt_rand(&passkey, sizeof(passkey))) { + return BT_SMP_ERR_UNSPECIFIED; + } + + passkey %= 1000000; } - passkey %= 1000000; + if (bt_auth && bt_auth->passkey_display) { + atomic_set_bit(smp->flags, SMP_FLAG_DISPLAY); + bt_auth->passkey_display(conn, passkey); + } - bt_auth->passkey_display(conn, passkey); - - passkey = sys_cpu_to_le32(passkey); - memcpy(smp->tk, &passkey, sizeof(passkey)); + sys_put_le32(passkey, smp->tk); break; case PASSKEY_INPUT: @@ -1978,7 +2002,7 @@ static u8_t legacy_pairing_req(struct bt_smp *smp, u8_t remote_io) smp->method = legacy_get_pair_method(smp, remote_io); /* ask for consent if pairing is not due to sending SecReq*/ - if (smp->method == JUST_WORKS && + if ((DISPLAY_FIXED(smp) || smp->method == JUST_WORKS) && !atomic_test_bit(smp->flags, SMP_FLAG_SEC_REQ) && bt_auth && bt_auth->pairing_confirm) { atomic_set_bit(smp->flags, SMP_FLAG_USER); @@ -2184,7 +2208,7 @@ static u8_t legacy_pairing_rsp(struct bt_smp *smp, u8_t remote_io) smp->method = legacy_get_pair_method(smp, remote_io); /* ask for consent if this is due to received SecReq */ - if (smp->method == JUST_WORKS && + if ((DISPLAY_FIXED(smp) || smp->method == JUST_WORKS) && atomic_test_bit(smp->flags, SMP_FLAG_SEC_REQ) && bt_auth && bt_auth->pairing_confirm) { atomic_set_bit(smp->flags, SMP_FLAG_USER); @@ -2404,23 +2428,16 @@ static u8_t smp_pairing_req(struct bt_smp *smp, struct net_buf *buf) smp->method = get_pair_method(smp, req->io_capability); - if (IS_ENABLED(CONFIG_BT_SMP_SC_ONLY) && - smp->method == JUST_WORKS) { + if (IS_ENABLED(CONFIG_BT_SMP_SC_ONLY) && smp->method == JUST_WORKS) { return BT_SMP_ERR_AUTH_REQUIREMENTS; } - if (smp->method == JUST_WORKS) { - if (IS_ENABLED(CONFIG_BT_SMP_SC_ONLY)) { - return BT_SMP_ERR_AUTH_REQUIREMENTS; - } - - /* ask for consent if pairing is not due to sending SecReq*/ - if (!atomic_test_bit(smp->flags, SMP_FLAG_SEC_REQ) && - bt_auth && bt_auth->pairing_confirm) { - atomic_set_bit(smp->flags, SMP_FLAG_USER); - bt_auth->pairing_confirm(smp->chan.chan.conn); - return 0; - } + if ((DISPLAY_FIXED(smp) || smp->method == JUST_WORKS) && + !atomic_test_bit(smp->flags, SMP_FLAG_SEC_REQ) && + bt_auth && bt_auth->pairing_confirm) { + atomic_set_bit(smp->flags, SMP_FLAG_USER); + bt_auth->pairing_confirm(smp->chan.chan.conn); + return 0; } atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_PUBLIC_KEY); @@ -2564,21 +2581,19 @@ static u8_t smp_pairing_rsp(struct bt_smp *smp, struct net_buf *buf) smp->method = get_pair_method(smp, rsp->io_capability); + if (IS_ENABLED(CONFIG_BT_SMP_SC_ONLY) && smp->method == JUST_WORKS) { + return BT_SMP_ERR_AUTH_REQUIREMENTS; + } + smp->local_dist &= SEND_KEYS_SC; smp->remote_dist &= RECV_KEYS_SC; - if (smp->method == JUST_WORKS) { - if (IS_ENABLED(CONFIG_BT_SMP_SC_ONLY)) { - return BT_SMP_ERR_AUTH_REQUIREMENTS; - } - - /* ask for consent if this is due to received SecReq */ - if (atomic_test_bit(smp->flags, SMP_FLAG_SEC_REQ) && - bt_auth && bt_auth->pairing_confirm) { - atomic_set_bit(smp->flags, SMP_FLAG_USER); - bt_auth->pairing_confirm(smp->chan.chan.conn); - return 0; - } + if ((DISPLAY_FIXED(smp) || smp->method == JUST_WORKS) && + atomic_test_bit(smp->flags, SMP_FLAG_SEC_REQ) && + bt_auth && bt_auth->pairing_confirm) { + atomic_set_bit(smp->flags, SMP_FLAG_USER); + bt_auth->pairing_confirm(smp->chan.chan.conn); + return 0; } if (!sc_local_pkey_valid) { @@ -3233,15 +3248,24 @@ static u8_t generate_dhkey(struct bt_smp *smp) static u8_t display_passkey(struct bt_smp *smp) { - if (bt_rand(&smp->passkey, sizeof(smp->passkey))) { - return BT_SMP_ERR_UNSPECIFIED; + 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))) { + return BT_SMP_ERR_UNSPECIFIED; + } + + smp->passkey %= 1000000; } - smp->passkey %= 1000000; smp->passkey_round = 0; - atomic_set_bit(smp->flags, SMP_FLAG_DISPLAY); - bt_auth->passkey_display(smp->chan.chan.conn, smp->passkey); + if (bt_auth && bt_auth->passkey_display) { + atomic_set_bit(smp->flags, SMP_FLAG_DISPLAY); + bt_auth->passkey_display(smp->chan.chan.conn, smp->passkey); + } + smp->passkey = sys_cpu_to_le32(smp->passkey); return 0; @@ -4353,6 +4377,23 @@ int bt_smp_auth_pairing_confirm(struct bt_conn *conn) } #endif /* !CONFIG_BT_SMP_SC_ONLY */ +#if defined(CONFIG_BT_FIXED_PASSKEY) +int bt_passkey_set(unsigned int passkey) +{ + if (passkey == BT_PASSKEY_INVALID) { + passkey = BT_PASSKEY_INVALID; + return 0; + } + + if (passkey > 999999) { + return -EINVAL; + } + + fixed_passkey = passkey; + return 0; +} +#endif /* CONFIG_SMP_FIXED_PASSKEY */ + void bt_smp_update_keys(struct bt_conn *conn) { struct bt_smp *smp;