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 <johan.hedberg@intel.com>
This commit is contained in:
Johan Hedberg 2018-07-30 20:29:40 +03:00 committed by Johan Hedberg
commit c446c8267b
4 changed files with 113 additions and 44 deletions

View file

@ -382,6 +382,28 @@ struct bt_conn_cb {
*/ */
void bt_conn_cb_register(struct bt_conn_cb *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 */ /** Authenticated pairing callback structure */
struct bt_conn_auth_cb { struct bt_conn_auth_cb {
/** @brief Display a passkey to the user. /** @brief Display a passkey to the user.

View file

@ -281,6 +281,13 @@ config BT_SMP_SC_ONLY
Security Mode 1 Level 4 stands for authenticated LE Secure Connections Security Mode 1 Level 4 stands for authenticated LE Secure Connections
pairing with encryption. Enabling this option disables legacy pairing. 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 config BT_USE_DEBUG_KEYS
bool "Enable Security Manager Debug Mode" bool "Enable Security Manager Debug Mode"
depends on BT_TINYCRYPT_ECC depends on BT_TINYCRYPT_ECC

View file

@ -1967,7 +1967,6 @@ int bt_conn_auth_cb_register(const struct bt_conn_auth_cb *cb)
return -EINVAL; return -EINVAL;
} }
bt_auth = cb; bt_auth = cb;
return 0; return 0;
} }

View file

@ -174,6 +174,12 @@ struct bt_smp {
struct k_delayed_work work; 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) #if !defined(CONFIG_BT_SMP_SC_ONLY)
/* based on table 2.8 Core Spec 2.3.5.1 Vol. 3 Part H */ /* 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 */] = { 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) static u8_t get_io_capa(void)
{ {
if (!bt_auth) { if (!bt_auth) {
return BT_SMP_IO_NO_INPUT_OUTPUT; goto no_callbacks;
} }
/* Passkey Confirmation is valid only for LE SC */ /* Passkey Confirmation is valid only for LE SC */
@ -260,14 +266,25 @@ static u8_t get_io_capa(void)
} }
if (bt_auth->passkey_entry) { 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) { if (bt_auth->passkey_display) {
return BT_SMP_IO_DISPLAY_ONLY; 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) 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) { switch (smp->method) {
case PASSKEY_DISPLAY: case PASSKEY_DISPLAY:
if (bt_rand(&passkey, sizeof(passkey))) { if (IS_ENABLED(CONFIG_BT_FIXED_PASSKEY) &&
return BT_SMP_ERR_UNSPECIFIED; 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); sys_put_le32(passkey, smp->tk);
passkey = sys_cpu_to_le32(passkey);
memcpy(smp->tk, &passkey, sizeof(passkey));
break; break;
case PASSKEY_INPUT: 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); smp->method = legacy_get_pair_method(smp, remote_io);
/* ask for consent if pairing is not due to sending SecReq*/ /* 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) && !atomic_test_bit(smp->flags, SMP_FLAG_SEC_REQ) &&
bt_auth && bt_auth->pairing_confirm) { bt_auth && bt_auth->pairing_confirm) {
atomic_set_bit(smp->flags, SMP_FLAG_USER); 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); smp->method = legacy_get_pair_method(smp, remote_io);
/* ask for consent if this is due to received SecReq */ /* 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) && atomic_test_bit(smp->flags, SMP_FLAG_SEC_REQ) &&
bt_auth && bt_auth->pairing_confirm) { bt_auth && bt_auth->pairing_confirm) {
atomic_set_bit(smp->flags, SMP_FLAG_USER); 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); smp->method = get_pair_method(smp, req->io_capability);
if (IS_ENABLED(CONFIG_BT_SMP_SC_ONLY) && if (IS_ENABLED(CONFIG_BT_SMP_SC_ONLY) && smp->method == JUST_WORKS) {
smp->method == JUST_WORKS) {
return BT_SMP_ERR_AUTH_REQUIREMENTS; return BT_SMP_ERR_AUTH_REQUIREMENTS;
} }
if (smp->method == JUST_WORKS) { if ((DISPLAY_FIXED(smp) || smp->method == JUST_WORKS) &&
if (IS_ENABLED(CONFIG_BT_SMP_SC_ONLY)) { !atomic_test_bit(smp->flags, SMP_FLAG_SEC_REQ) &&
return BT_SMP_ERR_AUTH_REQUIREMENTS; bt_auth && bt_auth->pairing_confirm) {
} atomic_set_bit(smp->flags, SMP_FLAG_USER);
bt_auth->pairing_confirm(smp->chan.chan.conn);
/* ask for consent if pairing is not due to sending SecReq*/ return 0;
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;
}
} }
atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_PUBLIC_KEY); 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); 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->local_dist &= SEND_KEYS_SC;
smp->remote_dist &= RECV_KEYS_SC; smp->remote_dist &= RECV_KEYS_SC;
if (smp->method == JUST_WORKS) { if ((DISPLAY_FIXED(smp) || smp->method == JUST_WORKS) &&
if (IS_ENABLED(CONFIG_BT_SMP_SC_ONLY)) { atomic_test_bit(smp->flags, SMP_FLAG_SEC_REQ) &&
return BT_SMP_ERR_AUTH_REQUIREMENTS; bt_auth && bt_auth->pairing_confirm) {
} atomic_set_bit(smp->flags, SMP_FLAG_USER);
bt_auth->pairing_confirm(smp->chan.chan.conn);
/* ask for consent if this is due to received SecReq */ return 0;
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 (!sc_local_pkey_valid) { 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) static u8_t display_passkey(struct bt_smp *smp)
{ {
if (bt_rand(&smp->passkey, sizeof(smp->passkey))) { if (IS_ENABLED(CONFIG_BT_FIXED_PASSKEY) &&
return BT_SMP_ERR_UNSPECIFIED; 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; smp->passkey_round = 0;
atomic_set_bit(smp->flags, SMP_FLAG_DISPLAY); if (bt_auth && bt_auth->passkey_display) {
bt_auth->passkey_display(smp->chan.chan.conn, smp->passkey); 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); smp->passkey = sys_cpu_to_le32(smp->passkey);
return 0; return 0;
@ -4353,6 +4377,23 @@ int bt_smp_auth_pairing_confirm(struct bt_conn *conn)
} }
#endif /* !CONFIG_BT_SMP_SC_ONLY */ #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) void bt_smp_update_keys(struct bt_conn *conn)
{ {
struct bt_smp *smp; struct bt_smp *smp;