87c8b897b3
Add functions to do XOR on arrays of memory, with one that takes arbitrary sizes and one for 32 bits and 128 bits as those are common sizes for this functionality. Signed-off-by: Emil Gydesen <emil.gydesen@nordicsemi.no>
5979 lines
150 KiB
C
5979 lines
150 KiB
C
/**
|
|
* @file smp.c
|
|
* Security Manager Protocol implementation
|
|
*/
|
|
|
|
/*
|
|
* Copyright (c) 2017 Nordic Semiconductor ASA
|
|
* Copyright (c) 2015-2016 Intel Corporation
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <stddef.h>
|
|
#include <string.h>
|
|
|
|
#include <zephyr/bluetooth/bluetooth.h>
|
|
#include <zephyr/bluetooth/buf.h>
|
|
#include <zephyr/bluetooth/conn.h>
|
|
#include <zephyr/bluetooth/hci.h>
|
|
#include <zephyr/debug/stack.h>
|
|
#include <zephyr/kernel.h>
|
|
#include <zephyr/net/buf.h>
|
|
#include <zephyr/sys/__assert.h>
|
|
#include <zephyr/sys/atomic.h>
|
|
#include <zephyr/sys/byteorder.h>
|
|
#include <zephyr/sys/check.h>
|
|
#include <zephyr/sys/util.h>
|
|
|
|
|
|
#include "common/bt_str.h"
|
|
|
|
#include "crypto/bt_crypto.h"
|
|
|
|
#include "hci_core.h"
|
|
#include "ecc.h"
|
|
#include "keys.h"
|
|
#include "conn_internal.h"
|
|
#include "l2cap_internal.h"
|
|
#include "smp.h"
|
|
|
|
#define LOG_LEVEL CONFIG_BT_SMP_LOG_LEVEL
|
|
#include <zephyr/logging/log.h>
|
|
LOG_MODULE_REGISTER(bt_smp);
|
|
|
|
#define SMP_TIMEOUT K_SECONDS(30)
|
|
|
|
#if defined(CONFIG_BT_SIGNING)
|
|
#define SIGN_DIST BT_SMP_DIST_SIGN
|
|
#else
|
|
#define SIGN_DIST 0
|
|
#endif
|
|
|
|
#if defined(CONFIG_BT_PRIVACY)
|
|
#define ID_DIST BT_SMP_DIST_ID_KEY
|
|
#else
|
|
#define ID_DIST 0
|
|
#endif
|
|
|
|
#if defined(CONFIG_BT_BREDR)
|
|
#define LINK_DIST BT_SMP_DIST_LINK_KEY
|
|
#else
|
|
#define LINK_DIST 0
|
|
#endif
|
|
|
|
#define RECV_KEYS (BT_SMP_DIST_ENC_KEY | BT_SMP_DIST_ID_KEY | SIGN_DIST |\
|
|
LINK_DIST)
|
|
#define SEND_KEYS (BT_SMP_DIST_ENC_KEY | ID_DIST | SIGN_DIST | LINK_DIST)
|
|
|
|
#define RECV_KEYS_SC (RECV_KEYS & ~(BT_SMP_DIST_ENC_KEY))
|
|
#define SEND_KEYS_SC (SEND_KEYS & ~(BT_SMP_DIST_ENC_KEY))
|
|
|
|
#define BR_RECV_KEYS_SC (RECV_KEYS & ~(LINK_DIST))
|
|
#define BR_SEND_KEYS_SC (SEND_KEYS & ~(LINK_DIST))
|
|
|
|
#define BT_SMP_AUTH_MASK 0x07
|
|
|
|
#if defined(CONFIG_BT_BONDABLE)
|
|
#define BT_SMP_AUTH_BONDING_FLAGS BT_SMP_AUTH_BONDING
|
|
#else
|
|
#define BT_SMP_AUTH_BONDING_FLAGS 0
|
|
#endif /* CONFIG_BT_BONDABLE */
|
|
|
|
#if defined(CONFIG_BT_BREDR)
|
|
|
|
#define BT_SMP_AUTH_MASK_SC 0x2f
|
|
#if defined(CONFIG_BT_SMP_OOB_LEGACY_PAIR_ONLY)
|
|
#define BT_SMP_AUTH_DEFAULT (BT_SMP_AUTH_BONDING_FLAGS | BT_SMP_AUTH_CT2)
|
|
#else
|
|
#define BT_SMP_AUTH_DEFAULT (BT_SMP_AUTH_BONDING_FLAGS | BT_SMP_AUTH_CT2 |\
|
|
BT_SMP_AUTH_SC)
|
|
#endif /* CONFIG_BT_SMP_OOB_LEGACY_PAIR_ONLY */
|
|
|
|
#else
|
|
|
|
#define BT_SMP_AUTH_MASK_SC 0x0f
|
|
#if defined(CONFIG_BT_SMP_OOB_LEGACY_PAIR_ONLY)
|
|
#define BT_SMP_AUTH_DEFAULT (BT_SMP_AUTH_BONDING_FLAGS)
|
|
#else
|
|
#define BT_SMP_AUTH_DEFAULT (BT_SMP_AUTH_BONDING_FLAGS | BT_SMP_AUTH_SC)
|
|
#endif /* CONFIG_BT_SMP_OOB_LEGACY_PAIR_ONLY */
|
|
|
|
#endif /* CONFIG_BT_BREDR */
|
|
|
|
enum pairing_method {
|
|
JUST_WORKS, /* JustWorks pairing */
|
|
PASSKEY_INPUT, /* Passkey Entry input */
|
|
PASSKEY_DISPLAY, /* Passkey Entry display */
|
|
PASSKEY_CONFIRM, /* Passkey confirm */
|
|
PASSKEY_ROLE, /* Passkey Entry depends on role */
|
|
LE_SC_OOB, /* LESC Out of Band */
|
|
LEGACY_OOB, /* Legacy Out of Band */
|
|
};
|
|
|
|
enum {
|
|
SMP_FLAG_CFM_DELAYED, /* if confirm should be send when TK is valid */
|
|
SMP_FLAG_ENC_PENDING, /* if waiting for an encryption change event */
|
|
SMP_FLAG_KEYS_DISTR, /* if keys distribution phase is in progress */
|
|
SMP_FLAG_PAIRING, /* if pairing is in progress */
|
|
SMP_FLAG_TIMEOUT, /* if SMP timeout occurred */
|
|
SMP_FLAG_SC, /* if LE Secure Connections is used */
|
|
SMP_FLAG_PKEY_SEND, /* if should send Public Key when available */
|
|
SMP_FLAG_DHKEY_PENDING, /* if waiting for local DHKey */
|
|
SMP_FLAG_DHKEY_GEN, /* if generating DHKey */
|
|
SMP_FLAG_DHKEY_SEND, /* if should generate and send DHKey Check */
|
|
SMP_FLAG_USER, /* if waiting for user input */
|
|
SMP_FLAG_DISPLAY, /* if display_passkey() callback was called */
|
|
SMP_FLAG_OOB_PENDING, /* if waiting for OOB data */
|
|
SMP_FLAG_BOND, /* if bonding */
|
|
SMP_FLAG_SC_DEBUG_KEY, /* if Secure Connection are using debug key */
|
|
SMP_FLAG_SEC_REQ, /* if Security Request was sent/received */
|
|
SMP_FLAG_DHCHECK_WAIT, /* if waiting for remote DHCheck (as periph) */
|
|
SMP_FLAG_DERIVE_LK, /* if Link Key should be derived */
|
|
SMP_FLAG_BR_CONNECTED, /* if BR/EDR channel is connected */
|
|
SMP_FLAG_BR_PAIR, /* if should start BR/EDR pairing */
|
|
SMP_FLAG_CT2, /* if should use H7 for keys derivation */
|
|
|
|
/* Total number of flags - must be at the end */
|
|
SMP_NUM_FLAGS,
|
|
};
|
|
|
|
/* SMP channel specific context */
|
|
struct bt_smp {
|
|
/* Commands that remote is allowed to send */
|
|
ATOMIC_DEFINE(allowed_cmds, BT_SMP_NUM_CMDS);
|
|
|
|
/* Flags for SMP state machine */
|
|
ATOMIC_DEFINE(flags, SMP_NUM_FLAGS);
|
|
|
|
/* Type of method used for pairing */
|
|
uint8_t method;
|
|
|
|
/* Pairing Request PDU */
|
|
uint8_t preq[7];
|
|
|
|
/* Pairing Response PDU */
|
|
uint8_t prsp[7];
|
|
|
|
/* Pairing Confirm PDU */
|
|
uint8_t pcnf[16];
|
|
|
|
/* Local random number */
|
|
uint8_t prnd[16];
|
|
|
|
/* Remote random number */
|
|
uint8_t rrnd[16];
|
|
|
|
/* Temporary key */
|
|
uint8_t tk[16];
|
|
|
|
/* Remote Public Key for LE SC */
|
|
uint8_t pkey[BT_PUB_KEY_LEN];
|
|
|
|
/* DHKey */
|
|
uint8_t dhkey[BT_DH_KEY_LEN];
|
|
|
|
/* Remote DHKey check */
|
|
uint8_t e[16];
|
|
|
|
/* MacKey */
|
|
uint8_t mackey[16];
|
|
|
|
/* LE SC passkey */
|
|
uint32_t passkey;
|
|
|
|
/* LE SC passkey round */
|
|
uint8_t passkey_round;
|
|
|
|
/* LE SC local OOB data */
|
|
const struct bt_le_oob_sc_data *oobd_local;
|
|
|
|
/* LE SC remote OOB data */
|
|
const struct bt_le_oob_sc_data *oobd_remote;
|
|
|
|
/* Local key distribution */
|
|
uint8_t local_dist;
|
|
|
|
/* Remote key distribution */
|
|
uint8_t remote_dist;
|
|
|
|
/* The channel this context is associated with.
|
|
* This marks the beginning of the part of the structure that will not
|
|
* be memset to zero in init.
|
|
*/
|
|
struct bt_l2cap_le_chan chan;
|
|
|
|
/* Delayed work for timeout handling */
|
|
struct k_work_delayable work;
|
|
|
|
/* Used Bluetooth authentication callbacks. */
|
|
atomic_ptr_t auth_cb;
|
|
|
|
/* Bondable flag */
|
|
atomic_t bondable;
|
|
};
|
|
|
|
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_PAIR_ONLY)
|
|
/* based on table 2.8 Core Spec 2.3.5.1 Vol. 3 Part H */
|
|
static const uint8_t gen_method_legacy[5 /* remote */][5 /* local */] = {
|
|
{ JUST_WORKS, JUST_WORKS, PASSKEY_INPUT, JUST_WORKS, PASSKEY_INPUT },
|
|
{ JUST_WORKS, JUST_WORKS, PASSKEY_INPUT, JUST_WORKS, PASSKEY_INPUT },
|
|
{ PASSKEY_DISPLAY, PASSKEY_DISPLAY, PASSKEY_INPUT, JUST_WORKS,
|
|
PASSKEY_DISPLAY },
|
|
{ JUST_WORKS, JUST_WORKS, JUST_WORKS, JUST_WORKS, JUST_WORKS },
|
|
{ PASSKEY_DISPLAY, PASSKEY_DISPLAY, PASSKEY_INPUT, JUST_WORKS,
|
|
PASSKEY_ROLE },
|
|
};
|
|
#endif /* CONFIG_BT_SMP_SC_PAIR_ONLY */
|
|
|
|
#if !defined(CONFIG_BT_SMP_OOB_LEGACY_PAIR_ONLY)
|
|
/* based on table 2.8 Core Spec 2.3.5.1 Vol. 3 Part H */
|
|
static const uint8_t gen_method_sc[5 /* remote */][5 /* local */] = {
|
|
{ JUST_WORKS, JUST_WORKS, PASSKEY_INPUT, JUST_WORKS, PASSKEY_INPUT },
|
|
{ JUST_WORKS, PASSKEY_CONFIRM, PASSKEY_INPUT, JUST_WORKS,
|
|
PASSKEY_CONFIRM },
|
|
{ PASSKEY_DISPLAY, PASSKEY_DISPLAY, PASSKEY_INPUT, JUST_WORKS,
|
|
PASSKEY_DISPLAY },
|
|
{ JUST_WORKS, JUST_WORKS, JUST_WORKS, JUST_WORKS, JUST_WORKS },
|
|
{ PASSKEY_DISPLAY, PASSKEY_CONFIRM, PASSKEY_INPUT, JUST_WORKS,
|
|
PASSKEY_CONFIRM },
|
|
};
|
|
#endif /* !CONFIG_BT_SMP_OOB_LEGACY_PAIR_ONLY */
|
|
|
|
#if defined(CONFIG_BT_BREDR)
|
|
/* SMP over BR/EDR channel specific context */
|
|
struct bt_smp_br {
|
|
/* Commands that remote is allowed to send */
|
|
ATOMIC_DEFINE(allowed_cmds, BT_SMP_NUM_CMDS);
|
|
|
|
/* Flags for SMP state machine */
|
|
ATOMIC_DEFINE(flags, SMP_NUM_FLAGS);
|
|
|
|
/* Local key distribution */
|
|
uint8_t local_dist;
|
|
|
|
/* Remote key distribution */
|
|
uint8_t remote_dist;
|
|
|
|
/* Encryption Key Size used for connection */
|
|
uint8_t enc_key_size;
|
|
|
|
/* The channel this context is associated with.
|
|
* This marks the beginning of the part of the structure that will not
|
|
* be memset to zero in init.
|
|
*/
|
|
struct bt_l2cap_br_chan chan;
|
|
|
|
/* Delayed work for timeout handling */
|
|
struct k_work_delayable work;
|
|
};
|
|
|
|
static struct bt_smp_br bt_smp_br_pool[CONFIG_BT_MAX_CONN];
|
|
#endif /* CONFIG_BT_BREDR */
|
|
|
|
static struct bt_smp bt_smp_pool[CONFIG_BT_MAX_CONN];
|
|
static bool bondable = IS_ENABLED(CONFIG_BT_BONDABLE);
|
|
static bool sc_oobd_present;
|
|
static bool legacy_oobd_present;
|
|
static bool sc_supported;
|
|
static const uint8_t *sc_public_key;
|
|
static K_SEM_DEFINE(sc_local_pkey_ready, 0, 1);
|
|
|
|
/* Pointer to internal data is used to mark that callbacks of given SMP channel are not initialized.
|
|
* Value of NULL represents no authenticaiton capabilities and cannot be used for that purpose.
|
|
*/
|
|
#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)
|
|
{
|
|
/*
|
|
* If controller based ECC is to be used it must support
|
|
* "LE Read Local P-256 Public Key" and "LE Generate DH Key" commands.
|
|
* Otherwise LE SC are not supported.
|
|
*/
|
|
if (IS_ENABLED(CONFIG_BT_SMP_OOB_LEGACY_PAIR_ONLY)) {
|
|
return false;
|
|
}
|
|
|
|
return BT_CMD_TEST(bt_dev.supported_commands, 34, 1) &&
|
|
BT_CMD_TEST(bt_dev.supported_commands, 34, 2);
|
|
}
|
|
|
|
static const struct bt_conn_auth_cb *latch_auth_cb(struct bt_smp *smp)
|
|
{
|
|
(void)atomic_ptr_cas(&smp->auth_cb, BT_SMP_AUTH_CB_UNINITIALIZED,
|
|
(atomic_ptr_val_t)bt_auth);
|
|
|
|
return atomic_ptr_get(&smp->auth_cb);
|
|
}
|
|
|
|
static bool latch_bondable(struct bt_smp *smp)
|
|
{
|
|
(void)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);
|
|
|
|
if (!smp_auth_cb) {
|
|
goto no_callbacks;
|
|
}
|
|
|
|
/* Passkey Confirmation is valid only for LE SC */
|
|
if (smp_auth_cb->passkey_display && smp_auth_cb->passkey_entry &&
|
|
(smp_auth_cb->passkey_confirm || !sc_supported)) {
|
|
return BT_SMP_IO_KEYBOARD_DISPLAY;
|
|
}
|
|
|
|
/* DisplayYesNo is useful only for LE SC */
|
|
if (sc_supported && smp_auth_cb->passkey_display &&
|
|
smp_auth_cb->passkey_confirm) {
|
|
return BT_SMP_IO_DISPLAY_YESNO;
|
|
}
|
|
|
|
if (smp_auth_cb->passkey_entry) {
|
|
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 (smp_auth_cb->passkey_display) {
|
|
return BT_SMP_IO_DISPLAY_ONLY;
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
#if !defined(CONFIG_BT_SMP_SC_PAIR_ONLY)
|
|
static uint8_t legacy_get_pair_method(struct bt_smp *smp, uint8_t remote_io);
|
|
#endif
|
|
|
|
static bool smp_keys_check(struct bt_conn *conn)
|
|
{
|
|
if (atomic_test_bit(conn->flags, BT_CONN_FORCE_PAIR)) {
|
|
return false;
|
|
}
|
|
|
|
if (!conn->le.keys) {
|
|
conn->le.keys = bt_keys_find(BT_KEYS_LTK_P256,
|
|
conn->id, &conn->le.dst);
|
|
if (!conn->le.keys) {
|
|
conn->le.keys = bt_keys_find(BT_KEYS_LTK,
|
|
conn->id,
|
|
&conn->le.dst);
|
|
}
|
|
}
|
|
|
|
if (!conn->le.keys ||
|
|
!(conn->le.keys->keys & (BT_KEYS_LTK | BT_KEYS_LTK_P256))) {
|
|
return false;
|
|
}
|
|
|
|
if (conn->required_sec_level >= BT_SECURITY_L3 &&
|
|
!(conn->le.keys->flags & BT_KEYS_AUTHENTICATED)) {
|
|
return false;
|
|
}
|
|
|
|
if (conn->required_sec_level >= BT_SECURITY_L4 &&
|
|
!((conn->le.keys->flags & BT_KEYS_AUTHENTICATED) &&
|
|
(conn->le.keys->keys & BT_KEYS_LTK_P256) &&
|
|
(conn->le.keys->enc_size == BT_SMP_MAX_ENC_KEY_SIZE))) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static uint8_t get_pair_method(struct bt_smp *smp, uint8_t remote_io)
|
|
{
|
|
#if !defined(CONFIG_BT_SMP_SC_PAIR_ONLY)
|
|
if (!atomic_test_bit(smp->flags, SMP_FLAG_SC)) {
|
|
return legacy_get_pair_method(smp, remote_io);
|
|
}
|
|
#endif
|
|
|
|
#if !defined(CONFIG_BT_SMP_OOB_LEGACY_PAIR_ONLY)
|
|
struct bt_smp_pairing *req, *rsp;
|
|
|
|
req = (struct bt_smp_pairing *)&smp->preq[1];
|
|
rsp = (struct bt_smp_pairing *)&smp->prsp[1];
|
|
|
|
if ((req->auth_req & rsp->auth_req) & BT_SMP_AUTH_SC) {
|
|
/* if one side has OOB data use OOB */
|
|
if ((req->oob_flag | rsp->oob_flag) & BT_SMP_OOB_DATA_MASK) {
|
|
return LE_SC_OOB;
|
|
}
|
|
}
|
|
|
|
if (remote_io > BT_SMP_IO_KEYBOARD_DISPLAY) {
|
|
return JUST_WORKS;
|
|
}
|
|
|
|
/* if none side requires MITM use JustWorks */
|
|
if (!((req->auth_req | rsp->auth_req) & BT_SMP_AUTH_MITM)) {
|
|
return JUST_WORKS;
|
|
}
|
|
|
|
return gen_method_sc[remote_io][get_io_capa(smp)];
|
|
#else
|
|
return JUST_WORKS;
|
|
#endif
|
|
}
|
|
|
|
static enum bt_security_err security_err_get(uint8_t smp_err)
|
|
{
|
|
switch (smp_err) {
|
|
case BT_SMP_ERR_PASSKEY_ENTRY_FAILED:
|
|
case BT_SMP_ERR_DHKEY_CHECK_FAILED:
|
|
case BT_SMP_ERR_NUMERIC_COMP_FAILED:
|
|
case BT_SMP_ERR_CONFIRM_FAILED:
|
|
return BT_SECURITY_ERR_AUTH_FAIL;
|
|
case BT_SMP_ERR_OOB_NOT_AVAIL:
|
|
return BT_SECURITY_ERR_OOB_NOT_AVAILABLE;
|
|
case BT_SMP_ERR_AUTH_REQUIREMENTS:
|
|
case BT_SMP_ERR_ENC_KEY_SIZE:
|
|
return BT_SECURITY_ERR_AUTH_REQUIREMENT;
|
|
case BT_SMP_ERR_PAIRING_NOTSUPP:
|
|
case BT_SMP_ERR_CMD_NOTSUPP:
|
|
return BT_SECURITY_ERR_PAIR_NOT_SUPPORTED;
|
|
case BT_SMP_ERR_REPEATED_ATTEMPTS:
|
|
case BT_SMP_ERR_BREDR_PAIRING_IN_PROGRESS:
|
|
case BT_SMP_ERR_CROSS_TRANSP_NOT_ALLOWED:
|
|
return BT_SECURITY_ERR_PAIR_NOT_ALLOWED;
|
|
case BT_SMP_ERR_INVALID_PARAMS:
|
|
return BT_SECURITY_ERR_INVALID_PARAM;
|
|
case BT_SMP_ERR_KEY_REJECTED:
|
|
return BT_SECURITY_ERR_KEY_REJECTED;
|
|
case BT_SMP_ERR_UNSPECIFIED:
|
|
default:
|
|
return BT_SECURITY_ERR_UNSPECIFIED;
|
|
}
|
|
}
|
|
|
|
static uint8_t smp_err_get(enum bt_security_err auth_err)
|
|
{
|
|
switch (auth_err) {
|
|
case BT_SECURITY_ERR_OOB_NOT_AVAILABLE:
|
|
return BT_SMP_ERR_OOB_NOT_AVAIL;
|
|
|
|
case BT_SECURITY_ERR_AUTH_FAIL:
|
|
case BT_SECURITY_ERR_AUTH_REQUIREMENT:
|
|
return BT_SMP_ERR_AUTH_REQUIREMENTS;
|
|
|
|
case BT_SECURITY_ERR_PAIR_NOT_SUPPORTED:
|
|
return BT_SMP_ERR_PAIRING_NOTSUPP;
|
|
|
|
case BT_SECURITY_ERR_INVALID_PARAM:
|
|
return BT_SMP_ERR_INVALID_PARAMS;
|
|
|
|
case BT_SECURITY_ERR_PIN_OR_KEY_MISSING:
|
|
case BT_SECURITY_ERR_PAIR_NOT_ALLOWED:
|
|
case BT_SECURITY_ERR_UNSPECIFIED:
|
|
return BT_SMP_ERR_UNSPECIFIED;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static struct net_buf *smp_create_pdu(struct bt_smp *smp, uint8_t op, size_t len)
|
|
{
|
|
struct bt_smp_hdr *hdr;
|
|
struct net_buf *buf;
|
|
k_timeout_t timeout;
|
|
|
|
/* Don't if session had already timed out */
|
|
if (atomic_test_bit(smp->flags, SMP_FLAG_TIMEOUT)) {
|
|
timeout = K_NO_WAIT;
|
|
} else {
|
|
timeout = SMP_TIMEOUT;
|
|
}
|
|
|
|
/* Use smaller timeout if returning an error since that could be
|
|
* caused by lack of buffers.
|
|
*/
|
|
buf = bt_l2cap_create_pdu_timeout(NULL, 0, timeout);
|
|
if (!buf) {
|
|
/* If it was not possible to allocate a buffer within the
|
|
* timeout marked it as timed out.
|
|
*/
|
|
atomic_set_bit(smp->flags, SMP_FLAG_TIMEOUT);
|
|
return NULL;
|
|
}
|
|
|
|
hdr = net_buf_add(buf, sizeof(*hdr));
|
|
hdr->code = op;
|
|
|
|
return buf;
|
|
}
|
|
|
|
static uint8_t get_encryption_key_size(struct bt_smp *smp)
|
|
{
|
|
struct bt_smp_pairing *req, *rsp;
|
|
|
|
req = (struct bt_smp_pairing *)&smp->preq[1];
|
|
rsp = (struct bt_smp_pairing *)&smp->prsp[1];
|
|
|
|
/*
|
|
* The smaller value of the initiating and responding devices maximum
|
|
* encryption key length parameters shall be used as the encryption key
|
|
* size.
|
|
*/
|
|
return MIN(req->max_key_size, rsp->max_key_size);
|
|
}
|
|
|
|
/* Check that if a new pairing procedure with an existing bond will not lower
|
|
* the established security level of the bond.
|
|
*/
|
|
static bool update_keys_check(struct bt_smp *smp, struct bt_keys *keys)
|
|
{
|
|
if (IS_ENABLED(CONFIG_BT_SMP_DISABLE_LEGACY_JW_PASSKEY) &&
|
|
!atomic_test_bit(smp->flags, SMP_FLAG_SC) &&
|
|
smp->method != LEGACY_OOB) {
|
|
return false;
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_SMP_OOB_LEGACY_PAIR_ONLY) &&
|
|
smp->method != LEGACY_OOB) {
|
|
return false;
|
|
}
|
|
|
|
if (!keys ||
|
|
!(keys->keys & (BT_KEYS_LTK_P256 | BT_KEYS_LTK))) {
|
|
return true;
|
|
}
|
|
|
|
if (keys->enc_size > get_encryption_key_size(smp)) {
|
|
return false;
|
|
}
|
|
|
|
if ((keys->keys & BT_KEYS_LTK_P256) &&
|
|
!atomic_test_bit(smp->flags, SMP_FLAG_SC)) {
|
|
return false;
|
|
}
|
|
|
|
if ((keys->flags & BT_KEYS_AUTHENTICATED) &&
|
|
smp->method == JUST_WORKS) {
|
|
return false;
|
|
}
|
|
|
|
if (!IS_ENABLED(CONFIG_BT_SMP_ALLOW_UNAUTH_OVERWRITE) &&
|
|
(!(keys->flags & BT_KEYS_AUTHENTICATED)
|
|
&& smp->method == JUST_WORKS)) {
|
|
if (!IS_ENABLED(CONFIG_BT_ID_ALLOW_UNAUTH_OVERWRITE) ||
|
|
(keys->id == smp->chan.chan.conn->id)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool update_debug_keys_check(struct bt_smp *smp)
|
|
{
|
|
struct bt_conn *conn = smp->chan.chan.conn;
|
|
|
|
if (!conn->le.keys) {
|
|
conn->le.keys = bt_keys_get_addr(conn->id, &conn->le.dst);
|
|
}
|
|
|
|
if (!conn->le.keys ||
|
|
!(conn->le.keys->keys & (BT_KEYS_LTK_P256 | BT_KEYS_LTK))) {
|
|
return true;
|
|
}
|
|
|
|
if (conn->le.keys->flags & BT_KEYS_DEBUG) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
#if defined(CONFIG_BT_PRIVACY) || defined(CONFIG_BT_SIGNING) || \
|
|
!defined(CONFIG_BT_SMP_SC_PAIR_ONLY)
|
|
/* For TX callbacks */
|
|
static void smp_pairing_complete(struct bt_smp *smp, uint8_t status);
|
|
#if defined(CONFIG_BT_BREDR)
|
|
static void smp_pairing_br_complete(struct bt_smp_br *smp, uint8_t status);
|
|
#endif
|
|
|
|
static void smp_check_complete(struct bt_conn *conn, uint8_t dist_complete)
|
|
{
|
|
struct bt_l2cap_chan *chan;
|
|
|
|
if (conn->type == BT_CONN_TYPE_LE) {
|
|
struct bt_smp *smp;
|
|
|
|
chan = bt_l2cap_le_lookup_tx_cid(conn, BT_L2CAP_CID_SMP);
|
|
__ASSERT(chan, "No SMP channel found");
|
|
|
|
smp = CONTAINER_OF(chan, struct bt_smp, chan.chan);
|
|
smp->local_dist &= ~dist_complete;
|
|
|
|
/* if all keys were distributed, pairing is done */
|
|
if (!smp->local_dist && !smp->remote_dist) {
|
|
smp_pairing_complete(smp, 0);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
#if defined(CONFIG_BT_BREDR)
|
|
if (conn->type == BT_CONN_TYPE_BR) {
|
|
struct bt_smp_br *smp;
|
|
|
|
chan = bt_l2cap_le_lookup_tx_cid(conn, BT_L2CAP_CID_BR_SMP);
|
|
__ASSERT(chan, "No SMP channel found");
|
|
|
|
smp = CONTAINER_OF(chan, struct bt_smp_br, chan.chan);
|
|
smp->local_dist &= ~dist_complete;
|
|
|
|
/* if all keys were distributed, pairing is done */
|
|
if (!smp->local_dist && !smp->remote_dist) {
|
|
smp_pairing_br_complete(smp, 0);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
#if defined(CONFIG_BT_PRIVACY)
|
|
static void smp_id_sent(struct bt_conn *conn, void *user_data, int err)
|
|
{
|
|
if (!err) {
|
|
smp_check_complete(conn, BT_SMP_DIST_ID_KEY);
|
|
}
|
|
}
|
|
#endif /* CONFIG_BT_PRIVACY */
|
|
|
|
#if defined(CONFIG_BT_SIGNING)
|
|
static void smp_sign_info_sent(struct bt_conn *conn, void *user_data, int err)
|
|
{
|
|
if (!err) {
|
|
smp_check_complete(conn, BT_SMP_DIST_SIGN);
|
|
}
|
|
}
|
|
#endif /* CONFIG_BT_SIGNING */
|
|
|
|
#if defined(CONFIG_BT_BREDR)
|
|
static void sc_derive_link_key(struct bt_smp *smp)
|
|
{
|
|
/* constants as specified in Core Spec Vol.3 Part H 2.4.2.4 */
|
|
static const uint8_t lebr[4] = { 0x72, 0x62, 0x65, 0x6c };
|
|
struct bt_conn *conn = smp->chan.chan.conn;
|
|
struct bt_keys_link_key *link_key;
|
|
uint8_t ilk[16];
|
|
|
|
LOG_DBG("");
|
|
|
|
/* TODO handle errors? */
|
|
|
|
/*
|
|
* At this point remote device identity is known so we can use
|
|
* destination address here
|
|
*/
|
|
link_key = bt_keys_get_link_key(&conn->le.dst.a);
|
|
if (!link_key) {
|
|
return;
|
|
}
|
|
|
|
if (atomic_test_bit(smp->flags, SMP_FLAG_CT2)) {
|
|
/* constants as specified in Core Spec Vol.3 Part H 2.4.2.4 */
|
|
static const uint8_t salt[16] = { 0x31, 0x70, 0x6d, 0x74,
|
|
0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00 };
|
|
|
|
if (bt_crypto_h7(salt, conn->le.keys->ltk.val, ilk)) {
|
|
bt_keys_link_key_clear(link_key);
|
|
return;
|
|
}
|
|
} else {
|
|
/* constants as specified in Core Spec Vol.3 Part H 2.4.2.4 */
|
|
static const uint8_t tmp1[4] = { 0x31, 0x70, 0x6d, 0x74 };
|
|
|
|
if (bt_crypto_h6(conn->le.keys->ltk.val, tmp1, ilk)) {
|
|
bt_keys_link_key_clear(link_key);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (bt_crypto_h6(ilk, lebr, link_key->val)) {
|
|
bt_keys_link_key_clear(link_key);
|
|
}
|
|
|
|
link_key->flags |= BT_LINK_KEY_SC;
|
|
|
|
if (conn->le.keys->flags & BT_KEYS_AUTHENTICATED) {
|
|
link_key->flags |= BT_LINK_KEY_AUTHENTICATED;
|
|
} else {
|
|
link_key->flags &= ~BT_LINK_KEY_AUTHENTICATED;
|
|
}
|
|
}
|
|
|
|
static void smp_br_reset(struct bt_smp_br *smp)
|
|
{
|
|
/* Clear flags first in case canceling of timeout fails. The SMP context
|
|
* shall be marked as timed out in that case.
|
|
*/
|
|
atomic_set(smp->flags, 0);
|
|
|
|
/* If canceling fails the timeout handler will set the timeout flag and
|
|
* mark the it as timed out. No new pairing procedures shall be started
|
|
* on this connection if that happens.
|
|
*/
|
|
(void)k_work_cancel_delayable(&smp->work);
|
|
|
|
atomic_set(smp->allowed_cmds, 0);
|
|
|
|
atomic_set_bit(smp->allowed_cmds, BT_SMP_CMD_PAIRING_REQ);
|
|
}
|
|
|
|
static void smp_pairing_br_complete(struct bt_smp_br *smp, uint8_t status)
|
|
{
|
|
struct bt_conn *conn = smp->chan.chan.conn;
|
|
struct bt_keys *keys;
|
|
bt_addr_le_t addr;
|
|
|
|
LOG_DBG("status 0x%x", status);
|
|
|
|
/* For dualmode devices LE address is same as BR/EDR address
|
|
* and is of public type.
|
|
*/
|
|
bt_addr_copy(&addr.a, &conn->br.dst);
|
|
addr.type = BT_ADDR_LE_PUBLIC;
|
|
keys = bt_keys_find_addr(conn->id, &addr);
|
|
|
|
if (status) {
|
|
struct bt_conn_auth_info_cb *listener, *next;
|
|
|
|
if (keys) {
|
|
bt_keys_clear(keys);
|
|
}
|
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&bt_auth_info_cbs, listener,
|
|
next, node) {
|
|
if (listener->pairing_failed) {
|
|
listener->pairing_failed(smp->chan.chan.conn,
|
|
security_err_get(status));
|
|
}
|
|
}
|
|
} else {
|
|
bool bond_flag = atomic_test_bit(smp->flags, SMP_FLAG_BOND);
|
|
struct bt_conn_auth_info_cb *listener, *next;
|
|
|
|
if (bond_flag && keys) {
|
|
bt_keys_store(keys);
|
|
}
|
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&bt_auth_info_cbs, listener,
|
|
next, node) {
|
|
if (listener->pairing_complete) {
|
|
listener->pairing_complete(smp->chan.chan.conn,
|
|
bond_flag);
|
|
}
|
|
}
|
|
}
|
|
|
|
smp_br_reset(smp);
|
|
}
|
|
|
|
static void smp_br_timeout(struct k_work *work)
|
|
{
|
|
struct k_work_delayable *dwork = k_work_delayable_from_work(work);
|
|
struct bt_smp_br *smp = CONTAINER_OF(dwork, struct bt_smp_br, work);
|
|
|
|
LOG_ERR("SMP Timeout");
|
|
|
|
smp_pairing_br_complete(smp, BT_SMP_ERR_UNSPECIFIED);
|
|
atomic_set_bit(smp->flags, SMP_FLAG_TIMEOUT);
|
|
}
|
|
|
|
static void smp_br_send(struct bt_smp_br *smp, struct net_buf *buf,
|
|
bt_conn_tx_cb_t cb)
|
|
{
|
|
int err = bt_l2cap_send_cb(smp->chan.chan.conn, BT_L2CAP_CID_BR_SMP, buf, cb, NULL);
|
|
|
|
if (err) {
|
|
if (err == -ENOBUFS) {
|
|
LOG_ERR("Ran out of TX buffers or contexts.");
|
|
}
|
|
|
|
net_buf_unref(buf);
|
|
return;
|
|
}
|
|
|
|
k_work_reschedule(&smp->work, SMP_TIMEOUT);
|
|
}
|
|
|
|
static void bt_smp_br_connected(struct bt_l2cap_chan *chan)
|
|
{
|
|
struct bt_smp_br *smp = CONTAINER_OF(chan, struct bt_smp_br, chan.chan);
|
|
|
|
LOG_DBG("chan %p cid 0x%04x", chan,
|
|
CONTAINER_OF(chan, struct bt_l2cap_br_chan, chan)->tx.cid);
|
|
|
|
atomic_set_bit(smp->flags, SMP_FLAG_BR_CONNECTED);
|
|
|
|
/*
|
|
* if this flag is set it means pairing was requested before channel
|
|
* was connected
|
|
*/
|
|
if (atomic_test_bit(smp->flags, SMP_FLAG_BR_PAIR)) {
|
|
bt_smp_br_send_pairing_req(chan->conn);
|
|
}
|
|
}
|
|
|
|
static void bt_smp_br_disconnected(struct bt_l2cap_chan *chan)
|
|
{
|
|
struct bt_smp_br *smp = CONTAINER_OF(chan, struct bt_smp_br, chan.chan);
|
|
|
|
LOG_DBG("chan %p cid 0x%04x", chan,
|
|
CONTAINER_OF(chan, struct bt_l2cap_br_chan, chan)->tx.cid);
|
|
|
|
/* Channel disconnected callback is always called from a work handler
|
|
* so canceling of the timeout work should always succeed.
|
|
*/
|
|
(void)k_work_cancel_delayable(&smp->work);
|
|
|
|
(void)memset(smp, 0, sizeof(*smp));
|
|
}
|
|
|
|
static void smp_br_init(struct bt_smp_br *smp)
|
|
{
|
|
/* Initialize SMP context exluding L2CAP channel context and anything
|
|
* else declared after.
|
|
*/
|
|
(void)memset(smp, 0, offsetof(struct bt_smp_br, chan));
|
|
|
|
atomic_set_bit(smp->allowed_cmds, BT_SMP_CMD_PAIRING_FAIL);
|
|
}
|
|
|
|
static void smp_br_derive_ltk(struct bt_smp_br *smp)
|
|
{
|
|
/* constants as specified in Core Spec Vol.3 Part H 2.4.2.5 */
|
|
static const uint8_t brle[4] = { 0x65, 0x6c, 0x72, 0x62 };
|
|
struct bt_conn *conn = smp->chan.chan.conn;
|
|
struct bt_keys_link_key *link_key = conn->br.link_key;
|
|
struct bt_keys *keys;
|
|
bt_addr_le_t addr;
|
|
uint8_t ilk[16];
|
|
|
|
LOG_DBG("");
|
|
|
|
if (!link_key) {
|
|
return;
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_SMP_FORCE_BREDR) && conn->encrypt != 0x02) {
|
|
LOG_WRN("Using P192 Link Key for P256 LTK derivation");
|
|
}
|
|
|
|
/*
|
|
* For dualmode devices LE address is same as BR/EDR address and is of
|
|
* public type.
|
|
*/
|
|
bt_addr_copy(&addr.a, &conn->br.dst);
|
|
addr.type = BT_ADDR_LE_PUBLIC;
|
|
|
|
keys = bt_keys_get_type(BT_KEYS_LTK_P256, conn->id, &addr);
|
|
if (!keys) {
|
|
LOG_ERR("Unable to get keys for %s", bt_addr_le_str(&addr));
|
|
return;
|
|
}
|
|
|
|
if (atomic_test_bit(smp->flags, SMP_FLAG_CT2)) {
|
|
/* constants as specified in Core Spec Vol.3 Part H 2.4.2.5 */
|
|
static const uint8_t salt[16] = { 0x32, 0x70, 0x6d, 0x74,
|
|
0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00 };
|
|
|
|
if (bt_crypto_h7(salt, link_key->val, ilk)) {
|
|
bt_keys_link_key_clear(link_key);
|
|
return;
|
|
}
|
|
} else {
|
|
/* constants as specified in Core Spec Vol.3 Part H 2.4.2.5 */
|
|
static const uint8_t tmp2[4] = { 0x32, 0x70, 0x6d, 0x74 };
|
|
|
|
if (bt_crypto_h6(link_key->val, tmp2, ilk)) {
|
|
bt_keys_clear(keys);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (bt_crypto_h6(ilk, brle, keys->ltk.val)) {
|
|
bt_keys_clear(keys);
|
|
return;
|
|
}
|
|
|
|
(void)memset(keys->ltk.ediv, 0, sizeof(keys->ltk.ediv));
|
|
(void)memset(keys->ltk.rand, 0, sizeof(keys->ltk.rand));
|
|
keys->enc_size = smp->enc_key_size;
|
|
|
|
if (link_key->flags & BT_LINK_KEY_AUTHENTICATED) {
|
|
keys->flags |= BT_KEYS_AUTHENTICATED;
|
|
} else {
|
|
keys->flags &= ~BT_KEYS_AUTHENTICATED;
|
|
}
|
|
|
|
LOG_DBG("LTK derived from LinkKey");
|
|
}
|
|
|
|
static struct net_buf *smp_br_create_pdu(struct bt_smp_br *smp, uint8_t op,
|
|
size_t len)
|
|
{
|
|
struct bt_smp_hdr *hdr;
|
|
struct net_buf *buf;
|
|
k_timeout_t timeout;
|
|
|
|
/* Don't if session had already timed out */
|
|
if (atomic_test_bit(smp->flags, SMP_FLAG_TIMEOUT)) {
|
|
timeout = K_NO_WAIT;
|
|
} else {
|
|
timeout = SMP_TIMEOUT;
|
|
}
|
|
|
|
/* Use smaller timeout if returning an error since that could be
|
|
* caused by lack of buffers.
|
|
*/
|
|
buf = bt_l2cap_create_pdu_timeout(NULL, 0, timeout);
|
|
if (!buf) {
|
|
/* If it was not possible to allocate a buffer within the
|
|
* timeout marked it as timed out.
|
|
*/
|
|
atomic_set_bit(smp->flags, SMP_FLAG_TIMEOUT);
|
|
return NULL;
|
|
}
|
|
|
|
hdr = net_buf_add(buf, sizeof(*hdr));
|
|
hdr->code = op;
|
|
|
|
return buf;
|
|
}
|
|
|
|
static void smp_br_distribute_keys(struct bt_smp_br *smp)
|
|
{
|
|
struct bt_conn *conn = smp->chan.chan.conn;
|
|
struct bt_keys *keys;
|
|
bt_addr_le_t addr;
|
|
|
|
/*
|
|
* For dualmode devices LE address is same as BR/EDR address and is of
|
|
* public type.
|
|
*/
|
|
bt_addr_copy(&addr.a, &conn->br.dst);
|
|
addr.type = BT_ADDR_LE_PUBLIC;
|
|
|
|
keys = bt_keys_get_addr(conn->id, &addr);
|
|
if (!keys) {
|
|
LOG_ERR("No keys space for %s", bt_addr_le_str(&addr));
|
|
return;
|
|
}
|
|
|
|
#if defined(CONFIG_BT_PRIVACY)
|
|
if (smp->local_dist & BT_SMP_DIST_ID_KEY) {
|
|
struct bt_smp_ident_info *id_info;
|
|
struct bt_smp_ident_addr_info *id_addr_info;
|
|
struct net_buf *buf;
|
|
|
|
smp->local_dist &= ~BT_SMP_DIST_ID_KEY;
|
|
|
|
buf = smp_br_create_pdu(smp, BT_SMP_CMD_IDENT_INFO,
|
|
sizeof(*id_info));
|
|
if (!buf) {
|
|
LOG_ERR("Unable to allocate Ident Info buffer");
|
|
return;
|
|
}
|
|
|
|
id_info = net_buf_add(buf, sizeof(*id_info));
|
|
memcpy(id_info->irk, bt_dev.irk[conn->id], 16);
|
|
|
|
smp_br_send(smp, buf, NULL);
|
|
|
|
buf = smp_br_create_pdu(smp, BT_SMP_CMD_IDENT_ADDR_INFO,
|
|
sizeof(*id_addr_info));
|
|
if (!buf) {
|
|
LOG_ERR("Unable to allocate Ident Addr Info buffer");
|
|
return;
|
|
}
|
|
|
|
id_addr_info = net_buf_add(buf, sizeof(*id_addr_info));
|
|
bt_addr_le_copy(&id_addr_info->addr, &bt_dev.id_addr[conn->id]);
|
|
|
|
smp_br_send(smp, buf, smp_id_sent);
|
|
}
|
|
#endif /* CONFIG_BT_PRIVACY */
|
|
|
|
#if defined(CONFIG_BT_SIGNING)
|
|
if (smp->local_dist & BT_SMP_DIST_SIGN) {
|
|
struct bt_smp_signing_info *info;
|
|
struct net_buf *buf;
|
|
|
|
smp->local_dist &= ~BT_SMP_DIST_SIGN;
|
|
|
|
buf = smp_br_create_pdu(smp, BT_SMP_CMD_SIGNING_INFO,
|
|
sizeof(*info));
|
|
if (!buf) {
|
|
LOG_ERR("Unable to allocate Signing Info buffer");
|
|
return;
|
|
}
|
|
|
|
info = net_buf_add(buf, sizeof(*info));
|
|
|
|
if (bt_rand(info->csrk, sizeof(info->csrk))) {
|
|
LOG_ERR("Unable to get random bytes");
|
|
return;
|
|
}
|
|
|
|
if (atomic_test_bit(smp->flags, SMP_FLAG_BOND)) {
|
|
bt_keys_add_type(keys, BT_KEYS_LOCAL_CSRK);
|
|
memcpy(keys->local_csrk.val, info->csrk, 16);
|
|
keys->local_csrk.cnt = 0U;
|
|
}
|
|
|
|
smp_br_send(smp, buf, smp_sign_info_sent);
|
|
}
|
|
#endif /* CONFIG_BT_SIGNING */
|
|
}
|
|
|
|
static bool smp_br_pairing_allowed(struct bt_smp_br *smp)
|
|
{
|
|
if (smp->chan.chan.conn->encrypt == 0x02) {
|
|
return true;
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_SMP_FORCE_BREDR) &&
|
|
smp->chan.chan.conn->encrypt == 0x01) {
|
|
LOG_WRN("Allowing BR/EDR SMP with P-192 key");
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static uint8_t smp_br_pairing_req(struct bt_smp_br *smp, struct net_buf *buf)
|
|
{
|
|
struct bt_smp_pairing *req = (void *)buf->data;
|
|
struct bt_conn *conn = smp->chan.chan.conn;
|
|
struct bt_smp_pairing *rsp;
|
|
struct net_buf *rsp_buf;
|
|
uint8_t max_key_size;
|
|
|
|
LOG_DBG("req: io_capability 0x%02X, oob_flag 0x%02X, auth_req 0x%02X, "
|
|
"max_key_size 0x%02X, init_key_dist 0x%02X, resp_key_dist 0x%02X",
|
|
req->io_capability, req->oob_flag, req->auth_req,
|
|
req->max_key_size, req->init_key_dist, req->resp_key_dist);
|
|
|
|
/*
|
|
* If a Pairing Request is received over the BR/EDR transport when
|
|
* either cross-transport key derivation/generation is not supported or
|
|
* the BR/EDR transport is not encrypted using a Link Key generated
|
|
* using P256, a Pairing Failed shall be sent with the error code
|
|
* "Cross-transport Key Derivation/Generation not allowed" (0x0E)."
|
|
*/
|
|
if (!smp_br_pairing_allowed(smp)) {
|
|
return BT_SMP_ERR_CROSS_TRANSP_NOT_ALLOWED;
|
|
}
|
|
|
|
max_key_size = bt_conn_enc_key_size(conn);
|
|
if (!max_key_size) {
|
|
LOG_DBG("Invalid encryption key size");
|
|
return BT_SMP_ERR_UNSPECIFIED;
|
|
}
|
|
|
|
if (req->max_key_size != max_key_size) {
|
|
return BT_SMP_ERR_ENC_KEY_SIZE;
|
|
}
|
|
|
|
rsp_buf = smp_br_create_pdu(smp, BT_SMP_CMD_PAIRING_RSP, sizeof(*rsp));
|
|
if (!rsp_buf) {
|
|
return BT_SMP_ERR_UNSPECIFIED;
|
|
}
|
|
|
|
smp_br_init(smp);
|
|
smp->enc_key_size = max_key_size;
|
|
|
|
/*
|
|
* If Secure Connections pairing has been initiated over BR/EDR, the IO
|
|
* Capability, OOB data flag and Auth Req fields of the SM Pairing
|
|
* Request/Response PDU shall be set to zero on transmission, and
|
|
* ignored on reception.
|
|
*/
|
|
rsp = net_buf_add(rsp_buf, sizeof(*rsp));
|
|
|
|
rsp->auth_req = 0x00;
|
|
rsp->io_capability = 0x00;
|
|
rsp->oob_flag = 0x00;
|
|
rsp->max_key_size = max_key_size;
|
|
rsp->init_key_dist = (req->init_key_dist & BR_RECV_KEYS_SC);
|
|
rsp->resp_key_dist = (req->resp_key_dist & BR_RECV_KEYS_SC);
|
|
|
|
smp->local_dist = rsp->resp_key_dist;
|
|
smp->remote_dist = rsp->init_key_dist;
|
|
|
|
LOG_DBG("rsp: io_capability 0x%02X, oob_flag 0x%02X, auth_req 0x%02X, "
|
|
"max_key_size 0x%02X, init_key_dist 0x%02X, resp_key_dist 0x%02X",
|
|
rsp->io_capability, rsp->oob_flag, rsp->auth_req,
|
|
rsp->max_key_size, rsp->init_key_dist, rsp->resp_key_dist);
|
|
|
|
smp_br_send(smp, rsp_buf, NULL);
|
|
|
|
atomic_set_bit(smp->flags, SMP_FLAG_PAIRING);
|
|
|
|
/* derive LTK if requested and clear distribution bits */
|
|
if ((smp->local_dist & BT_SMP_DIST_ENC_KEY) &&
|
|
(smp->remote_dist & BT_SMP_DIST_ENC_KEY)) {
|
|
smp_br_derive_ltk(smp);
|
|
}
|
|
smp->local_dist &= ~BT_SMP_DIST_ENC_KEY;
|
|
smp->remote_dist &= ~BT_SMP_DIST_ENC_KEY;
|
|
|
|
/* BR/EDR acceptor is like LE Peripheral and distributes keys first */
|
|
smp_br_distribute_keys(smp);
|
|
|
|
if (smp->remote_dist & BT_SMP_DIST_ID_KEY) {
|
|
atomic_set_bit(smp->allowed_cmds, BT_SMP_CMD_IDENT_INFO);
|
|
} else if (smp->remote_dist & BT_SMP_DIST_SIGN) {
|
|
atomic_set_bit(smp->allowed_cmds, BT_SMP_CMD_SIGNING_INFO);
|
|
}
|
|
|
|
/* if all keys were distributed, pairing is done */
|
|
if (!smp->local_dist && !smp->remote_dist) {
|
|
smp_pairing_br_complete(smp, 0);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static uint8_t smp_br_pairing_rsp(struct bt_smp_br *smp, struct net_buf *buf)
|
|
{
|
|
struct bt_smp_pairing *rsp = (void *)buf->data;
|
|
struct bt_conn *conn = smp->chan.chan.conn;
|
|
uint8_t max_key_size;
|
|
|
|
LOG_DBG("rsp: io_capability 0x%02X, oob_flag 0x%02X, auth_req 0x%02X, "
|
|
"max_key_size 0x%02X, init_key_dist 0x%02X, resp_key_dist 0x%02X",
|
|
rsp->io_capability, rsp->oob_flag, rsp->auth_req,
|
|
rsp->max_key_size, rsp->init_key_dist, rsp->resp_key_dist);
|
|
|
|
max_key_size = bt_conn_enc_key_size(conn);
|
|
if (!max_key_size) {
|
|
LOG_DBG("Invalid encryption key size");
|
|
return BT_SMP_ERR_UNSPECIFIED;
|
|
}
|
|
|
|
if (rsp->max_key_size != max_key_size) {
|
|
return BT_SMP_ERR_ENC_KEY_SIZE;
|
|
}
|
|
|
|
smp->local_dist &= rsp->init_key_dist;
|
|
smp->remote_dist &= rsp->resp_key_dist;
|
|
|
|
smp->local_dist &= SEND_KEYS_SC;
|
|
smp->remote_dist &= RECV_KEYS_SC;
|
|
|
|
/* Peripheral distributes its keys first */
|
|
|
|
if (smp->remote_dist & BT_SMP_DIST_ID_KEY) {
|
|
atomic_set_bit(smp->allowed_cmds, BT_SMP_CMD_IDENT_INFO);
|
|
} else if (smp->remote_dist & BT_SMP_DIST_SIGN) {
|
|
atomic_set_bit(smp->allowed_cmds, BT_SMP_CMD_SIGNING_INFO);
|
|
}
|
|
|
|
/* derive LTK if requested and clear distribution bits */
|
|
if ((smp->local_dist & BT_SMP_DIST_ENC_KEY) &&
|
|
(smp->remote_dist & BT_SMP_DIST_ENC_KEY)) {
|
|
smp_br_derive_ltk(smp);
|
|
}
|
|
smp->local_dist &= ~BT_SMP_DIST_ENC_KEY;
|
|
smp->remote_dist &= ~BT_SMP_DIST_ENC_KEY;
|
|
|
|
/* Pairing acceptor distributes it's keys first */
|
|
if (smp->remote_dist) {
|
|
return 0;
|
|
}
|
|
|
|
smp_br_distribute_keys(smp);
|
|
|
|
/* if all keys were distributed, pairing is done */
|
|
if (!smp->local_dist && !smp->remote_dist) {
|
|
smp_pairing_br_complete(smp, 0);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static uint8_t smp_br_pairing_failed(struct bt_smp_br *smp, struct net_buf *buf)
|
|
{
|
|
struct bt_smp_pairing_fail *req = (void *)buf->data;
|
|
|
|
LOG_ERR("pairing failed (peer reason 0x%x)", req->reason);
|
|
|
|
smp_pairing_br_complete(smp, req->reason);
|
|
smp_br_reset(smp);
|
|
|
|
/* return no error to avoid sending Pairing Failed in response */
|
|
return 0;
|
|
}
|
|
|
|
static uint8_t smp_br_ident_info(struct bt_smp_br *smp, struct net_buf *buf)
|
|
{
|
|
struct bt_smp_ident_info *req = (void *)buf->data;
|
|
struct bt_conn *conn = smp->chan.chan.conn;
|
|
struct bt_keys *keys;
|
|
bt_addr_le_t addr;
|
|
|
|
LOG_DBG("");
|
|
|
|
/* TODO should we resolve LE address if matching RPA is connected? */
|
|
|
|
/*
|
|
* For dualmode devices LE address is same as BR/EDR address and is of
|
|
* public type.
|
|
*/
|
|
bt_addr_copy(&addr.a, &conn->br.dst);
|
|
addr.type = BT_ADDR_LE_PUBLIC;
|
|
|
|
keys = bt_keys_get_type(BT_KEYS_IRK, conn->id, &addr);
|
|
if (!keys) {
|
|
LOG_ERR("Unable to get keys for %s", bt_addr_le_str(&addr));
|
|
return BT_SMP_ERR_UNSPECIFIED;
|
|
}
|
|
|
|
memcpy(keys->irk.val, req->irk, sizeof(keys->irk.val));
|
|
|
|
atomic_set_bit(smp->allowed_cmds, BT_SMP_CMD_IDENT_ADDR_INFO);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static uint8_t smp_br_ident_addr_info(struct bt_smp_br *smp,
|
|
struct net_buf *buf)
|
|
{
|
|
struct bt_conn *conn = smp->chan.chan.conn;
|
|
struct bt_smp_ident_addr_info *req = (void *)buf->data;
|
|
bt_addr_le_t addr;
|
|
|
|
LOG_DBG("identity %s", bt_addr_le_str(&req->addr));
|
|
|
|
/*
|
|
* For dual mode device identity address must be same as BR/EDR address
|
|
* and be of public type. So if received one doesn't match BR/EDR
|
|
* address we fail.
|
|
*/
|
|
|
|
bt_addr_copy(&addr.a, &conn->br.dst);
|
|
addr.type = BT_ADDR_LE_PUBLIC;
|
|
|
|
if (!bt_addr_le_eq(&addr, &req->addr)) {
|
|
return BT_SMP_ERR_UNSPECIFIED;
|
|
}
|
|
|
|
smp->remote_dist &= ~BT_SMP_DIST_ID_KEY;
|
|
|
|
if (smp->remote_dist & BT_SMP_DIST_SIGN) {
|
|
atomic_set_bit(smp->allowed_cmds, BT_SMP_CMD_SIGNING_INFO);
|
|
}
|
|
|
|
if (conn->role == BT_CONN_ROLE_CENTRAL && !smp->remote_dist) {
|
|
smp_br_distribute_keys(smp);
|
|
}
|
|
|
|
/* if all keys were distributed, pairing is done */
|
|
if (!smp->local_dist && !smp->remote_dist) {
|
|
smp_pairing_br_complete(smp, 0);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if defined(CONFIG_BT_SIGNING)
|
|
static uint8_t smp_br_signing_info(struct bt_smp_br *smp, struct net_buf *buf)
|
|
{
|
|
struct bt_smp_signing_info *req = (void *)buf->data;
|
|
struct bt_conn *conn = smp->chan.chan.conn;
|
|
struct bt_keys *keys;
|
|
bt_addr_le_t addr;
|
|
|
|
LOG_DBG("");
|
|
|
|
/*
|
|
* For dualmode devices LE address is same as BR/EDR address and is of
|
|
* public type.
|
|
*/
|
|
bt_addr_copy(&addr.a, &conn->br.dst);
|
|
addr.type = BT_ADDR_LE_PUBLIC;
|
|
|
|
keys = bt_keys_get_type(BT_KEYS_REMOTE_CSRK, conn->id, &addr);
|
|
if (!keys) {
|
|
LOG_ERR("Unable to get keys for %s", bt_addr_le_str(&addr));
|
|
return BT_SMP_ERR_UNSPECIFIED;
|
|
}
|
|
|
|
memcpy(keys->remote_csrk.val, req->csrk, sizeof(keys->remote_csrk.val));
|
|
|
|
smp->remote_dist &= ~BT_SMP_DIST_SIGN;
|
|
|
|
if (conn->role == BT_CONN_ROLE_CENTRAL && !smp->remote_dist) {
|
|
smp_br_distribute_keys(smp);
|
|
}
|
|
|
|
/* if all keys were distributed, pairing is done */
|
|
if (!smp->local_dist && !smp->remote_dist) {
|
|
smp_pairing_br_complete(smp, 0);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#else
|
|
static uint8_t smp_br_signing_info(struct bt_smp_br *smp, struct net_buf *buf)
|
|
{
|
|
return BT_SMP_ERR_CMD_NOTSUPP;
|
|
}
|
|
#endif /* CONFIG_BT_SIGNING */
|
|
|
|
static const struct {
|
|
uint8_t (*func)(struct bt_smp_br *smp, struct net_buf *buf);
|
|
uint8_t expect_len;
|
|
} br_handlers[] = {
|
|
{ }, /* No op-code defined for 0x00 */
|
|
{ smp_br_pairing_req, sizeof(struct bt_smp_pairing) },
|
|
{ smp_br_pairing_rsp, sizeof(struct bt_smp_pairing) },
|
|
{ }, /* pairing confirm not used over BR/EDR */
|
|
{ }, /* pairing random not used over BR/EDR */
|
|
{ smp_br_pairing_failed, sizeof(struct bt_smp_pairing_fail) },
|
|
{ }, /* encrypt info not used over BR/EDR */
|
|
{ }, /* central ident not used over BR/EDR */
|
|
{ smp_br_ident_info, sizeof(struct bt_smp_ident_info) },
|
|
{ smp_br_ident_addr_info, sizeof(struct bt_smp_ident_addr_info) },
|
|
{ smp_br_signing_info, sizeof(struct bt_smp_signing_info) },
|
|
/* security request not used over BR/EDR */
|
|
/* public key not used over BR/EDR */
|
|
/* DHKey check not used over BR/EDR */
|
|
};
|
|
|
|
static int smp_br_error(struct bt_smp_br *smp, uint8_t reason)
|
|
{
|
|
struct bt_smp_pairing_fail *rsp;
|
|
struct net_buf *buf;
|
|
|
|
/* reset context and report */
|
|
smp_br_reset(smp);
|
|
|
|
buf = smp_br_create_pdu(smp, BT_SMP_CMD_PAIRING_FAIL, sizeof(*rsp));
|
|
if (!buf) {
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
rsp = net_buf_add(buf, sizeof(*rsp));
|
|
rsp->reason = reason;
|
|
|
|
/*
|
|
* SMP timer is not restarted for PairingFailed so don't use
|
|
* smp_br_send
|
|
*/
|
|
if (bt_l2cap_send(smp->chan.chan.conn, BT_L2CAP_CID_SMP, buf)) {
|
|
net_buf_unref(buf);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int bt_smp_br_recv(struct bt_l2cap_chan *chan, struct net_buf *buf)
|
|
{
|
|
struct bt_smp_br *smp = CONTAINER_OF(chan, struct bt_smp_br, chan.chan);
|
|
struct bt_smp_hdr *hdr;
|
|
uint8_t err;
|
|
|
|
if (buf->len < sizeof(*hdr)) {
|
|
LOG_ERR("Too small SMP PDU received");
|
|
return 0;
|
|
}
|
|
|
|
hdr = net_buf_pull_mem(buf, sizeof(*hdr));
|
|
LOG_DBG("Received SMP code 0x%02x len %u", hdr->code, buf->len);
|
|
|
|
/*
|
|
* If SMP timeout occurred "no further SMP commands shall be sent over
|
|
* the L2CAP Security Manager Channel. A new SM procedure shall only be
|
|
* performed when a new physical link has been established."
|
|
*/
|
|
if (atomic_test_bit(smp->flags, SMP_FLAG_TIMEOUT)) {
|
|
LOG_WRN("SMP command (code 0x%02x) received after timeout", hdr->code);
|
|
return 0;
|
|
}
|
|
|
|
if (hdr->code >= ARRAY_SIZE(br_handlers) ||
|
|
!br_handlers[hdr->code].func) {
|
|
LOG_WRN("Unhandled SMP code 0x%02x", hdr->code);
|
|
smp_br_error(smp, BT_SMP_ERR_CMD_NOTSUPP);
|
|
return 0;
|
|
}
|
|
|
|
if (!atomic_test_and_clear_bit(smp->allowed_cmds, hdr->code)) {
|
|
LOG_WRN("Unexpected SMP code 0x%02x", hdr->code);
|
|
smp_br_error(smp, BT_SMP_ERR_UNSPECIFIED);
|
|
return 0;
|
|
}
|
|
|
|
if (buf->len != br_handlers[hdr->code].expect_len) {
|
|
LOG_ERR("Invalid len %u for code 0x%02x", buf->len, hdr->code);
|
|
smp_br_error(smp, BT_SMP_ERR_INVALID_PARAMS);
|
|
return 0;
|
|
}
|
|
|
|
err = br_handlers[hdr->code].func(smp, buf);
|
|
if (err) {
|
|
smp_br_error(smp, err);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static bool br_sc_supported(void)
|
|
{
|
|
if (IS_ENABLED(CONFIG_BT_SMP_FORCE_BREDR)) {
|
|
LOG_WRN("Enabling BR/EDR SMP without BR/EDR SC support");
|
|
return true;
|
|
}
|
|
|
|
return BT_FEAT_SC(bt_dev.features);
|
|
}
|
|
|
|
static int bt_smp_br_accept(struct bt_conn *conn, struct bt_l2cap_chan **chan)
|
|
{
|
|
static const struct bt_l2cap_chan_ops ops = {
|
|
.connected = bt_smp_br_connected,
|
|
.disconnected = bt_smp_br_disconnected,
|
|
.recv = bt_smp_br_recv,
|
|
};
|
|
int i;
|
|
|
|
/* Check BR/EDR SC is supported */
|
|
if (!br_sc_supported()) {
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
LOG_DBG("conn %p handle %u", conn, conn->handle);
|
|
|
|
for (i = 0; i < ARRAY_SIZE(bt_smp_pool); i++) {
|
|
struct bt_smp_br *smp = &bt_smp_br_pool[i];
|
|
|
|
if (smp->chan.chan.conn) {
|
|
continue;
|
|
}
|
|
|
|
smp->chan.chan.ops = &ops;
|
|
|
|
*chan = &smp->chan.chan;
|
|
|
|
k_work_init_delayable(&smp->work, smp_br_timeout);
|
|
smp_br_reset(smp);
|
|
|
|
return 0;
|
|
}
|
|
|
|
LOG_ERR("No available SMP context for conn %p", conn);
|
|
|
|
return -ENOMEM;
|
|
}
|
|
|
|
static struct bt_smp_br *smp_br_chan_get(struct bt_conn *conn)
|
|
{
|
|
struct bt_l2cap_chan *chan;
|
|
|
|
chan = bt_l2cap_br_lookup_rx_cid(conn, BT_L2CAP_CID_BR_SMP);
|
|
if (!chan) {
|
|
LOG_ERR("Unable to find SMP channel");
|
|
return NULL;
|
|
}
|
|
|
|
return CONTAINER_OF(chan, struct bt_smp_br, chan.chan);
|
|
}
|
|
|
|
int bt_smp_br_send_pairing_req(struct bt_conn *conn)
|
|
{
|
|
struct bt_smp_pairing *req;
|
|
struct net_buf *req_buf;
|
|
uint8_t max_key_size;
|
|
struct bt_smp_br *smp;
|
|
|
|
smp = smp_br_chan_get(conn);
|
|
if (!smp) {
|
|
return -ENOTCONN;
|
|
}
|
|
|
|
/* SMP Timeout */
|
|
if (atomic_test_bit(smp->flags, SMP_FLAG_TIMEOUT)) {
|
|
return -EIO;
|
|
}
|
|
|
|
/* pairing is in progress */
|
|
if (atomic_test_bit(smp->flags, SMP_FLAG_PAIRING)) {
|
|
return -EBUSY;
|
|
}
|
|
|
|
/* check if we are allowed to start SMP over BR/EDR */
|
|
if (!smp_br_pairing_allowed(smp)) {
|
|
return 0;
|
|
}
|
|
|
|
/* Channel not yet connected, will start pairing once connected */
|
|
if (!atomic_test_bit(smp->flags, SMP_FLAG_BR_CONNECTED)) {
|
|
atomic_set_bit(smp->flags, SMP_FLAG_BR_PAIR);
|
|
return 0;
|
|
}
|
|
|
|
max_key_size = bt_conn_enc_key_size(conn);
|
|
if (!max_key_size) {
|
|
LOG_DBG("Invalid encryption key size");
|
|
return -EIO;
|
|
}
|
|
|
|
smp_br_init(smp);
|
|
smp->enc_key_size = max_key_size;
|
|
|
|
req_buf = smp_br_create_pdu(smp, BT_SMP_CMD_PAIRING_REQ, sizeof(*req));
|
|
if (!req_buf) {
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
req = net_buf_add(req_buf, sizeof(*req));
|
|
|
|
/*
|
|
* If Secure Connections pairing has been initiated over BR/EDR, the IO
|
|
* Capability, OOB data flag and Auth Req fields of the SM Pairing
|
|
* Request/Response PDU shall be set to zero on transmission, and
|
|
* ignored on reception.
|
|
*/
|
|
|
|
req->auth_req = 0x00;
|
|
req->io_capability = 0x00;
|
|
req->oob_flag = 0x00;
|
|
req->max_key_size = max_key_size;
|
|
req->init_key_dist = BR_SEND_KEYS_SC;
|
|
req->resp_key_dist = BR_RECV_KEYS_SC;
|
|
|
|
smp_br_send(smp, req_buf, NULL);
|
|
|
|
smp->local_dist = BR_SEND_KEYS_SC;
|
|
smp->remote_dist = BR_RECV_KEYS_SC;
|
|
|
|
atomic_set_bit(smp->allowed_cmds, BT_SMP_CMD_PAIRING_RSP);
|
|
|
|
atomic_set_bit(smp->flags, SMP_FLAG_PAIRING);
|
|
|
|
return 0;
|
|
}
|
|
#endif /* CONFIG_BT_BREDR */
|
|
|
|
static void smp_reset(struct bt_smp *smp)
|
|
{
|
|
struct bt_conn *conn = smp->chan.chan.conn;
|
|
|
|
/* Clear flags first in case canceling of timeout fails. The SMP context
|
|
* shall be marked as timed out in that case.
|
|
*/
|
|
atomic_set(smp->flags, 0);
|
|
|
|
/* If canceling fails the timeout handler will set the timeout flag and
|
|
* mark the it as timed out. No new pairing procedures shall be started
|
|
* on this connection if that happens.
|
|
*/
|
|
(void)k_work_cancel_delayable(&smp->work);
|
|
|
|
smp->method = JUST_WORKS;
|
|
atomic_set(smp->allowed_cmds, 0);
|
|
|
|
if (IS_ENABLED(CONFIG_BT_CENTRAL) &&
|
|
conn->role == BT_HCI_ROLE_CENTRAL) {
|
|
atomic_set_bit(smp->allowed_cmds, BT_SMP_CMD_SECURITY_REQUEST);
|
|
return;
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_PERIPHERAL)) {
|
|
atomic_set_bit(smp->allowed_cmds, BT_SMP_CMD_PAIRING_REQ);
|
|
}
|
|
}
|
|
|
|
static uint8_t hci_err_get(enum bt_security_err err)
|
|
{
|
|
switch (err) {
|
|
case BT_SECURITY_ERR_SUCCESS:
|
|
return BT_HCI_ERR_SUCCESS;
|
|
case BT_SECURITY_ERR_AUTH_FAIL:
|
|
return BT_HCI_ERR_AUTH_FAIL;
|
|
case BT_SECURITY_ERR_PIN_OR_KEY_MISSING:
|
|
return BT_HCI_ERR_PIN_OR_KEY_MISSING;
|
|
case BT_SECURITY_ERR_PAIR_NOT_SUPPORTED:
|
|
return BT_HCI_ERR_PAIRING_NOT_SUPPORTED;
|
|
case BT_SECURITY_ERR_PAIR_NOT_ALLOWED:
|
|
return BT_HCI_ERR_PAIRING_NOT_ALLOWED;
|
|
case BT_SECURITY_ERR_INVALID_PARAM:
|
|
return BT_HCI_ERR_INVALID_PARAM;
|
|
default:
|
|
return BT_HCI_ERR_UNSPECIFIED;
|
|
}
|
|
}
|
|
|
|
/* Note: This function not only does set the status but also calls smp_reset
|
|
* at the end which clears any flags previously set.
|
|
*/
|
|
static void smp_pairing_complete(struct bt_smp *smp, uint8_t status)
|
|
{
|
|
struct bt_conn *conn = smp->chan.chan.conn;
|
|
|
|
LOG_DBG("got status 0x%x", status);
|
|
|
|
if (conn->le.keys == NULL) {
|
|
/* We can get here if the application calls `bt_unpair` in the
|
|
* `security_changed` callback.
|
|
*/
|
|
LOG_WRN("The in-progress pairing has been deleted!");
|
|
status = BT_SMP_ERR_UNSPECIFIED;
|
|
}
|
|
|
|
if (!status) {
|
|
#if defined(CONFIG_BT_BREDR)
|
|
/*
|
|
* Don't derive if Debug Keys are used.
|
|
* TODO should we allow this if BR/EDR is already connected?
|
|
*/
|
|
if (atomic_test_bit(smp->flags, SMP_FLAG_DERIVE_LK) &&
|
|
(!atomic_test_bit(smp->flags, SMP_FLAG_SC_DEBUG_KEY) ||
|
|
IS_ENABLED(CONFIG_BT_STORE_DEBUG_KEYS))) {
|
|
sc_derive_link_key(smp);
|
|
}
|
|
#endif /* CONFIG_BT_BREDR */
|
|
bool bond_flag = atomic_test_bit(smp->flags, SMP_FLAG_BOND);
|
|
struct bt_conn_auth_info_cb *listener, *next;
|
|
|
|
if (IS_ENABLED(CONFIG_BT_LOG_SNIFFER_INFO)) {
|
|
bt_keys_show_sniffer_info(conn->le.keys, NULL);
|
|
}
|
|
|
|
if (bond_flag && conn->le.keys) {
|
|
bt_keys_store(conn->le.keys);
|
|
}
|
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&bt_auth_info_cbs, listener,
|
|
next, node) {
|
|
if (listener->pairing_complete) {
|
|
listener->pairing_complete(conn, bond_flag);
|
|
}
|
|
}
|
|
} else {
|
|
enum bt_security_err security_err = security_err_get(status);
|
|
|
|
/* Clear the key pool entry in case of pairing failure if the
|
|
* keys already existed before the pairing procedure or the
|
|
* pairing failed during key distribution.
|
|
*/
|
|
if (conn->le.keys &&
|
|
(!conn->le.keys->enc_size ||
|
|
atomic_test_bit(smp->flags, SMP_FLAG_KEYS_DISTR))) {
|
|
bt_keys_clear(conn->le.keys);
|
|
conn->le.keys = NULL;
|
|
}
|
|
|
|
if (!atomic_test_bit(smp->flags, SMP_FLAG_KEYS_DISTR)) {
|
|
bt_conn_security_changed(conn,
|
|
hci_err_get(security_err),
|
|
security_err);
|
|
}
|
|
|
|
/* Check SMP_FLAG_PAIRING as bt_conn_security_changed may
|
|
* have called the pairing_failed callback already.
|
|
*/
|
|
if (atomic_test_bit(smp->flags, SMP_FLAG_PAIRING)) {
|
|
struct bt_conn_auth_info_cb *listener, *next;
|
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&bt_auth_info_cbs,
|
|
listener, next,
|
|
node) {
|
|
if (listener->pairing_failed) {
|
|
listener->pairing_failed(conn, security_err);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
smp_reset(smp);
|
|
|
|
if (conn->state == BT_CONN_CONNECTED && conn->sec_level != conn->required_sec_level) {
|
|
bt_smp_start_security(conn);
|
|
}
|
|
}
|
|
|
|
static void smp_timeout(struct k_work *work)
|
|
{
|
|
struct k_work_delayable *dwork = k_work_delayable_from_work(work);
|
|
struct bt_smp *smp = CONTAINER_OF(dwork, struct bt_smp, work);
|
|
|
|
LOG_ERR("SMP Timeout");
|
|
|
|
smp_pairing_complete(smp, BT_SMP_ERR_UNSPECIFIED);
|
|
|
|
/* smp_pairing_complete clears flags so setting timeout flag must come
|
|
* after it.
|
|
*/
|
|
atomic_set_bit(smp->flags, SMP_FLAG_TIMEOUT);
|
|
}
|
|
|
|
static void smp_send(struct bt_smp *smp, struct net_buf *buf,
|
|
bt_conn_tx_cb_t cb, void *user_data)
|
|
{
|
|
int err = bt_l2cap_send_cb(smp->chan.chan.conn, BT_L2CAP_CID_SMP, buf, cb, NULL);
|
|
|
|
if (err) {
|
|
if (err == -ENOBUFS) {
|
|
LOG_ERR("Ran out of TX buffers or contexts.");
|
|
}
|
|
|
|
net_buf_unref(buf);
|
|
return;
|
|
}
|
|
|
|
k_work_reschedule(&smp->work, SMP_TIMEOUT);
|
|
}
|
|
|
|
static int smp_error(struct bt_smp *smp, uint8_t reason)
|
|
{
|
|
struct bt_smp_pairing_fail *rsp;
|
|
struct net_buf *buf;
|
|
bool remote_already_completed;
|
|
|
|
/* By spec, SMP "pairing process" completes successfully when the last
|
|
* key to distribute is acknowledged at link-layer.
|
|
*/
|
|
remote_already_completed = (atomic_test_bit(smp->flags, SMP_FLAG_KEYS_DISTR) &&
|
|
!smp->local_dist && !smp->remote_dist);
|
|
|
|
if (atomic_test_bit(smp->flags, SMP_FLAG_PAIRING) ||
|
|
atomic_test_bit(smp->flags, SMP_FLAG_ENC_PENDING) ||
|
|
atomic_test_bit(smp->flags, SMP_FLAG_SEC_REQ)) {
|
|
/* reset context and report */
|
|
smp_pairing_complete(smp, reason);
|
|
}
|
|
|
|
if (remote_already_completed) {
|
|
LOG_WRN("SMP does not allow a pairing failure at this point. Known issue. "
|
|
"Disconnecting instead.");
|
|
/* We are probably here because we are, as a peripheral, rejecting a pairing based
|
|
* on the central's identity address information, but that was the last key to
|
|
* be transmitted. In that case, the pairing process is already completed.
|
|
* The SMP protocol states that the pairing process is completed the moment the
|
|
* peripheral link-layer confirmed the reception of the PDU with the last key.
|
|
*/
|
|
bt_conn_disconnect(smp->chan.chan.conn, BT_HCI_ERR_AUTH_FAIL);
|
|
return 0;
|
|
}
|
|
|
|
buf = smp_create_pdu(smp, BT_SMP_CMD_PAIRING_FAIL, sizeof(*rsp));
|
|
if (!buf) {
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
rsp = net_buf_add(buf, sizeof(*rsp));
|
|
rsp->reason = reason;
|
|
|
|
/* SMP timer is not restarted for PairingFailed so don't use smp_send */
|
|
if (bt_l2cap_send(smp->chan.chan.conn, BT_L2CAP_CID_SMP, buf)) {
|
|
net_buf_unref(buf);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static uint8_t smp_send_pairing_random(struct bt_smp *smp)
|
|
{
|
|
struct bt_smp_pairing_random *req;
|
|
struct net_buf *rsp_buf;
|
|
|
|
rsp_buf = smp_create_pdu(smp, BT_SMP_CMD_PAIRING_RANDOM, sizeof(*req));
|
|
if (!rsp_buf) {
|
|
return BT_SMP_ERR_UNSPECIFIED;
|
|
}
|
|
|
|
req = net_buf_add(rsp_buf, sizeof(*req));
|
|
memcpy(req->val, smp->prnd, sizeof(req->val));
|
|
|
|
smp_send(smp, rsp_buf, NULL, NULL);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if !defined(CONFIG_BT_SMP_SC_PAIR_ONLY)
|
|
static int smp_c1(const uint8_t k[16], const uint8_t r[16],
|
|
const uint8_t preq[7], const uint8_t pres[7],
|
|
const bt_addr_le_t *ia, const bt_addr_le_t *ra,
|
|
uint8_t enc_data[16])
|
|
{
|
|
uint8_t p1[16], p2[16];
|
|
int err;
|
|
|
|
LOG_DBG("k %s", bt_hex(k, 16));
|
|
LOG_DBG("r %s", bt_hex(r, 16));
|
|
LOG_DBG("ia %s", bt_addr_le_str(ia));
|
|
LOG_DBG("ra %s", bt_addr_le_str(ra));
|
|
LOG_DBG("preq %s", bt_hex(preq, 7));
|
|
LOG_DBG("pres %s", bt_hex(pres, 7));
|
|
|
|
/* pres, preq, rat and iat are concatenated to generate p1 */
|
|
p1[0] = ia->type;
|
|
p1[1] = ra->type;
|
|
memcpy(p1 + 2, preq, 7);
|
|
memcpy(p1 + 9, pres, 7);
|
|
|
|
LOG_DBG("p1 %s", bt_hex(p1, 16));
|
|
|
|
/* c1 = e(k, e(k, r XOR p1) XOR p2) */
|
|
|
|
/* Using enc_data as temporary output buffer */
|
|
mem_xor_128(enc_data, r, p1);
|
|
|
|
err = bt_encrypt_le(k, enc_data, enc_data);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
/* ra is concatenated with ia and padding to generate p2 */
|
|
memcpy(p2, ra->a.val, 6);
|
|
memcpy(p2 + 6, ia->a.val, 6);
|
|
(void)memset(p2 + 12, 0, 4);
|
|
|
|
LOG_DBG("p2 %s", bt_hex(p2, 16));
|
|
|
|
mem_xor_128(enc_data, p2, enc_data);
|
|
|
|
return bt_encrypt_le(k, enc_data, enc_data);
|
|
}
|
|
#endif /* !CONFIG_BT_SMP_SC_PAIR_ONLY */
|
|
|
|
static uint8_t smp_send_pairing_confirm(struct bt_smp *smp)
|
|
{
|
|
struct bt_smp_pairing_confirm *req;
|
|
struct net_buf *buf;
|
|
uint8_t r;
|
|
|
|
switch (smp->method) {
|
|
case PASSKEY_CONFIRM:
|
|
case JUST_WORKS:
|
|
r = 0U;
|
|
break;
|
|
case PASSKEY_DISPLAY:
|
|
case PASSKEY_INPUT:
|
|
/*
|
|
* In the Passkey Entry protocol, the most significant
|
|
* bit of Z is set equal to one and the least
|
|
* significant bit is made up from one bit of the
|
|
* passkey e.g. if the passkey bit is 1, then Z = 0x81
|
|
* and if the passkey bit is 0, then Z = 0x80.
|
|
*/
|
|
r = (smp->passkey >> smp->passkey_round) & 0x01;
|
|
r |= 0x80;
|
|
break;
|
|
default:
|
|
LOG_ERR("Unknown pairing method (%u)", smp->method);
|
|
return BT_SMP_ERR_UNSPECIFIED;
|
|
}
|
|
|
|
buf = smp_create_pdu(smp, BT_SMP_CMD_PAIRING_CONFIRM, sizeof(*req));
|
|
if (!buf) {
|
|
return BT_SMP_ERR_UNSPECIFIED;
|
|
}
|
|
|
|
req = net_buf_add(buf, sizeof(*req));
|
|
|
|
if (bt_crypto_f4(sc_public_key, smp->pkey, smp->prnd, r, req->val)) {
|
|
net_buf_unref(buf);
|
|
return BT_SMP_ERR_UNSPECIFIED;
|
|
}
|
|
|
|
smp_send(smp, buf, NULL, NULL);
|
|
|
|
atomic_clear_bit(smp->flags, SMP_FLAG_CFM_DELAYED);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if !defined(CONFIG_BT_SMP_SC_PAIR_ONLY)
|
|
static void smp_ident_sent(struct bt_conn *conn, void *user_data, int err)
|
|
{
|
|
if (!err) {
|
|
smp_check_complete(conn, BT_SMP_DIST_ENC_KEY);
|
|
}
|
|
}
|
|
|
|
static void legacy_distribute_keys(struct bt_smp *smp)
|
|
{
|
|
struct bt_conn *conn = smp->chan.chan.conn;
|
|
struct bt_keys *keys = conn->le.keys;
|
|
|
|
if (smp->local_dist & BT_SMP_DIST_ENC_KEY) {
|
|
struct bt_smp_encrypt_info *info;
|
|
struct bt_smp_central_ident *ident;
|
|
struct net_buf *buf;
|
|
/* Use struct to get randomness in single call to bt_rand */
|
|
struct {
|
|
uint8_t key[16];
|
|
uint8_t rand[8];
|
|
uint8_t ediv[2];
|
|
} rand;
|
|
|
|
if (bt_rand((void *)&rand, sizeof(rand))) {
|
|
LOG_ERR("Unable to get random bytes");
|
|
return;
|
|
}
|
|
|
|
buf = smp_create_pdu(smp, BT_SMP_CMD_ENCRYPT_INFO,
|
|
sizeof(*info));
|
|
if (!buf) {
|
|
LOG_ERR("Unable to allocate Encrypt Info buffer");
|
|
return;
|
|
}
|
|
|
|
info = net_buf_add(buf, sizeof(*info));
|
|
|
|
/* distributed only enc_size bytes of key */
|
|
memcpy(info->ltk, rand.key, keys->enc_size);
|
|
if (keys->enc_size < sizeof(info->ltk)) {
|
|
(void)memset(info->ltk + keys->enc_size, 0,
|
|
sizeof(info->ltk) - keys->enc_size);
|
|
}
|
|
|
|
smp_send(smp, buf, NULL, NULL);
|
|
|
|
buf = smp_create_pdu(smp, BT_SMP_CMD_CENTRAL_IDENT,
|
|
sizeof(*ident));
|
|
if (!buf) {
|
|
LOG_ERR("Unable to allocate Central Ident buffer");
|
|
return;
|
|
}
|
|
|
|
ident = net_buf_add(buf, sizeof(*ident));
|
|
memcpy(ident->rand, rand.rand, sizeof(ident->rand));
|
|
memcpy(ident->ediv, rand.ediv, sizeof(ident->ediv));
|
|
|
|
smp_send(smp, buf, smp_ident_sent, NULL);
|
|
|
|
if (atomic_test_bit(smp->flags, SMP_FLAG_BOND)) {
|
|
bt_keys_add_type(keys, BT_KEYS_PERIPH_LTK);
|
|
|
|
memcpy(keys->periph_ltk.val, rand.key,
|
|
sizeof(keys->periph_ltk.val));
|
|
memcpy(keys->periph_ltk.rand, rand.rand,
|
|
sizeof(keys->periph_ltk.rand));
|
|
memcpy(keys->periph_ltk.ediv, rand.ediv,
|
|
sizeof(keys->periph_ltk.ediv));
|
|
}
|
|
}
|
|
}
|
|
#endif /* !CONFIG_BT_SMP_SC_PAIR_ONLY */
|
|
|
|
static uint8_t bt_smp_distribute_keys(struct bt_smp *smp)
|
|
{
|
|
struct bt_conn *conn = smp->chan.chan.conn;
|
|
struct bt_keys *keys = conn->le.keys;
|
|
|
|
if (!keys) {
|
|
LOG_ERR("No keys space for %s", bt_addr_le_str(&conn->le.dst));
|
|
return BT_SMP_ERR_UNSPECIFIED;
|
|
}
|
|
|
|
#if !defined(CONFIG_BT_SMP_SC_PAIR_ONLY)
|
|
/* Distribute legacy pairing specific keys */
|
|
if (!atomic_test_bit(smp->flags, SMP_FLAG_SC)) {
|
|
legacy_distribute_keys(smp);
|
|
}
|
|
#endif /* !CONFIG_BT_SMP_SC_PAIR_ONLY */
|
|
|
|
#if defined(CONFIG_BT_PRIVACY)
|
|
if (smp->local_dist & BT_SMP_DIST_ID_KEY) {
|
|
struct bt_smp_ident_info *id_info;
|
|
struct bt_smp_ident_addr_info *id_addr_info;
|
|
struct net_buf *buf;
|
|
|
|
buf = smp_create_pdu(smp, BT_SMP_CMD_IDENT_INFO,
|
|
sizeof(*id_info));
|
|
if (!buf) {
|
|
LOG_ERR("Unable to allocate Ident Info buffer");
|
|
return BT_SMP_ERR_UNSPECIFIED;
|
|
}
|
|
|
|
id_info = net_buf_add(buf, sizeof(*id_info));
|
|
memcpy(id_info->irk, bt_dev.irk[conn->id], 16);
|
|
|
|
smp_send(smp, buf, NULL, NULL);
|
|
|
|
buf = smp_create_pdu(smp, BT_SMP_CMD_IDENT_ADDR_INFO,
|
|
sizeof(*id_addr_info));
|
|
if (!buf) {
|
|
LOG_ERR("Unable to allocate Ident Addr Info buffer");
|
|
return BT_SMP_ERR_UNSPECIFIED;
|
|
}
|
|
|
|
id_addr_info = net_buf_add(buf, sizeof(*id_addr_info));
|
|
bt_addr_le_copy(&id_addr_info->addr, &bt_dev.id_addr[conn->id]);
|
|
|
|
smp_send(smp, buf, smp_id_sent, NULL);
|
|
}
|
|
#endif /* CONFIG_BT_PRIVACY */
|
|
|
|
#if defined(CONFIG_BT_SIGNING)
|
|
if (smp->local_dist & BT_SMP_DIST_SIGN) {
|
|
struct bt_smp_signing_info *info;
|
|
struct net_buf *buf;
|
|
|
|
buf = smp_create_pdu(smp, BT_SMP_CMD_SIGNING_INFO,
|
|
sizeof(*info));
|
|
if (!buf) {
|
|
LOG_ERR("Unable to allocate Signing Info buffer");
|
|
return BT_SMP_ERR_UNSPECIFIED;
|
|
}
|
|
|
|
info = net_buf_add(buf, sizeof(*info));
|
|
|
|
if (bt_rand(info->csrk, sizeof(info->csrk))) {
|
|
return BT_SMP_ERR_UNSPECIFIED;
|
|
}
|
|
|
|
if (atomic_test_bit(smp->flags, SMP_FLAG_BOND)) {
|
|
bt_keys_add_type(keys, BT_KEYS_LOCAL_CSRK);
|
|
memcpy(keys->local_csrk.val, info->csrk, 16);
|
|
keys->local_csrk.cnt = 0U;
|
|
}
|
|
|
|
smp_send(smp, buf, smp_sign_info_sent, NULL);
|
|
}
|
|
#endif /* CONFIG_BT_SIGNING */
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if defined(CONFIG_BT_PERIPHERAL)
|
|
static uint8_t send_pairing_rsp(struct bt_smp *smp)
|
|
{
|
|
struct bt_smp_pairing *rsp;
|
|
struct net_buf *rsp_buf;
|
|
|
|
rsp_buf = smp_create_pdu(smp, BT_SMP_CMD_PAIRING_RSP, sizeof(*rsp));
|
|
if (!rsp_buf) {
|
|
return BT_SMP_ERR_UNSPECIFIED;
|
|
}
|
|
|
|
rsp = net_buf_add(rsp_buf, sizeof(*rsp));
|
|
memcpy(rsp, smp->prsp + 1, sizeof(*rsp));
|
|
|
|
smp_send(smp, rsp_buf, NULL, NULL);
|
|
|
|
return 0;
|
|
}
|
|
#endif /* CONFIG_BT_PERIPHERAL */
|
|
|
|
static uint8_t smp_pairing_accept_query(struct bt_smp *smp, struct bt_smp_pairing *pairing)
|
|
{
|
|
#if defined(CONFIG_BT_SMP_APP_PAIRING_ACCEPT)
|
|
const struct bt_conn_auth_cb *smp_auth_cb = latch_auth_cb(smp);
|
|
struct bt_conn *conn = smp->chan.chan.conn;
|
|
|
|
if (smp_auth_cb && smp_auth_cb->pairing_accept) {
|
|
const struct bt_conn_pairing_feat feat = {
|
|
.io_capability = pairing->io_capability,
|
|
.oob_data_flag = pairing->oob_flag,
|
|
.auth_req = pairing->auth_req,
|
|
.max_enc_key_size = pairing->max_key_size,
|
|
.init_key_dist = pairing->init_key_dist,
|
|
.resp_key_dist = pairing->resp_key_dist
|
|
};
|
|
|
|
return smp_err_get(smp_auth_cb->pairing_accept(conn, &feat));
|
|
}
|
|
#endif /* CONFIG_BT_SMP_APP_PAIRING_ACCEPT */
|
|
return 0;
|
|
}
|
|
|
|
#if !defined(CONFIG_BT_SMP_SC_PAIR_ONLY)
|
|
static int smp_s1(const uint8_t k[16], const uint8_t r1[16],
|
|
const uint8_t r2[16], uint8_t out[16])
|
|
{
|
|
/* The most significant 64-bits of r1 are discarded to generate
|
|
* r1' and the most significant 64-bits of r2 are discarded to
|
|
* generate r2'.
|
|
* r1' is concatenated with r2' to generate r' which is used as
|
|
* the 128-bit input parameter plaintextData to security function e:
|
|
*
|
|
* r' = r1' || r2'
|
|
*/
|
|
memcpy(out, r2, 8);
|
|
memcpy(out + 8, r1, 8);
|
|
|
|
/* s1(k, r1 , r2) = e(k, r') */
|
|
return bt_encrypt_le(k, out, out);
|
|
}
|
|
|
|
static uint8_t legacy_get_pair_method(struct bt_smp *smp, uint8_t remote_io)
|
|
{
|
|
struct bt_smp_pairing *req, *rsp;
|
|
uint8_t method;
|
|
|
|
if (remote_io > BT_SMP_IO_KEYBOARD_DISPLAY) {
|
|
return JUST_WORKS;
|
|
}
|
|
|
|
req = (struct bt_smp_pairing *)&smp->preq[1];
|
|
rsp = (struct bt_smp_pairing *)&smp->prsp[1];
|
|
|
|
/* if both sides have OOB data use OOB */
|
|
if ((req->oob_flag & rsp->oob_flag) & BT_SMP_OOB_DATA_MASK) {
|
|
return LEGACY_OOB;
|
|
}
|
|
|
|
/* if none side requires MITM use JustWorks */
|
|
if (!((req->auth_req | rsp->auth_req) & BT_SMP_AUTH_MITM)) {
|
|
return JUST_WORKS;
|
|
}
|
|
|
|
method = gen_method_legacy[remote_io][get_io_capa(smp)];
|
|
|
|
/* if both sides have KeyboardDisplay capabilities, initiator displays
|
|
* and responder inputs
|
|
*/
|
|
if (method == PASSKEY_ROLE) {
|
|
if (smp->chan.chan.conn->role == BT_HCI_ROLE_CENTRAL) {
|
|
method = PASSKEY_DISPLAY;
|
|
} else {
|
|
method = PASSKEY_INPUT;
|
|
}
|
|
}
|
|
|
|
return method;
|
|
}
|
|
|
|
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
|
|
* distributed in new pairing. This is to avoid replacing authenticated
|
|
* keys with unauthenticated ones.
|
|
*/
|
|
keys = bt_keys_find_addr(conn->id, &conn->le.dst);
|
|
if (keys && (keys->flags & BT_KEYS_AUTHENTICATED) &&
|
|
smp->method == JUST_WORKS) {
|
|
LOG_ERR("JustWorks failed, authenticated keys present");
|
|
return BT_SMP_ERR_UNSPECIFIED;
|
|
}
|
|
|
|
switch (smp->method) {
|
|
case LEGACY_OOB:
|
|
if (smp_auth_cb && smp_auth_cb->oob_data_request) {
|
|
struct bt_conn_oob_info info = {
|
|
.type = BT_CONN_OOB_LE_LEGACY,
|
|
};
|
|
|
|
atomic_set_bit(smp->flags, SMP_FLAG_USER);
|
|
smp_auth_cb->oob_data_request(smp->chan.chan.conn, &info);
|
|
} else {
|
|
return BT_SMP_ERR_OOB_NOT_AVAIL;
|
|
}
|
|
|
|
break;
|
|
case PASSKEY_DISPLAY:
|
|
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;
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_LOG_SNIFFER_INFO)) {
|
|
LOG_INF("Legacy passkey %u", passkey);
|
|
}
|
|
|
|
if (smp_auth_cb && smp_auth_cb->passkey_display) {
|
|
atomic_set_bit(smp->flags, SMP_FLAG_DISPLAY);
|
|
smp_auth_cb->passkey_display(conn, passkey);
|
|
}
|
|
|
|
sys_put_le32(passkey, smp->tk);
|
|
|
|
break;
|
|
case PASSKEY_INPUT:
|
|
atomic_set_bit(smp->flags, SMP_FLAG_USER);
|
|
smp_auth_cb->passkey_entry(conn);
|
|
break;
|
|
case JUST_WORKS:
|
|
break;
|
|
default:
|
|
LOG_ERR("Unknown pairing method (%u)", smp->method);
|
|
return BT_SMP_ERR_UNSPECIFIED;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static uint8_t legacy_send_pairing_confirm(struct bt_smp *smp)
|
|
{
|
|
struct bt_conn *conn = smp->chan.chan.conn;
|
|
struct bt_smp_pairing_confirm *req;
|
|
struct net_buf *buf;
|
|
|
|
buf = smp_create_pdu(smp, BT_SMP_CMD_PAIRING_CONFIRM, sizeof(*req));
|
|
if (!buf) {
|
|
return BT_SMP_ERR_UNSPECIFIED;
|
|
}
|
|
|
|
req = net_buf_add(buf, sizeof(*req));
|
|
|
|
if (smp_c1(smp->tk, smp->prnd, smp->preq, smp->prsp,
|
|
&conn->le.init_addr, &conn->le.resp_addr, req->val)) {
|
|
net_buf_unref(buf);
|
|
return BT_SMP_ERR_UNSPECIFIED;
|
|
}
|
|
|
|
smp_send(smp, buf, NULL, NULL);
|
|
|
|
atomic_clear_bit(smp->flags, SMP_FLAG_CFM_DELAYED);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if defined(CONFIG_BT_PERIPHERAL)
|
|
static uint8_t legacy_pairing_req(struct bt_smp *smp)
|
|
{
|
|
const struct bt_conn_auth_cb *smp_auth_cb = latch_auth_cb(smp);
|
|
uint8_t ret;
|
|
|
|
LOG_DBG("");
|
|
|
|
ret = legacy_request_tk(smp);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
|
|
/* ask for consent if pairing is not due to sending SecReq*/
|
|
if ((DISPLAY_FIXED(smp) || smp->method == JUST_WORKS) &&
|
|
!atomic_test_bit(smp->flags, SMP_FLAG_SEC_REQ) &&
|
|
smp_auth_cb && smp_auth_cb->pairing_confirm) {
|
|
atomic_set_bit(smp->flags, SMP_FLAG_USER);
|
|
smp_auth_cb->pairing_confirm(smp->chan.chan.conn);
|
|
return 0;
|
|
}
|
|
|
|
atomic_set_bit(smp->allowed_cmds, BT_SMP_CMD_PAIRING_CONFIRM);
|
|
atomic_set_bit(smp->allowed_cmds, BT_SMP_KEYPRESS_NOTIFICATION);
|
|
return send_pairing_rsp(smp);
|
|
}
|
|
#endif /* CONFIG_BT_PERIPHERAL */
|
|
|
|
static uint8_t legacy_pairing_random(struct bt_smp *smp)
|
|
{
|
|
struct bt_conn *conn = smp->chan.chan.conn;
|
|
uint8_t tmp[16];
|
|
int err;
|
|
|
|
LOG_DBG("");
|
|
|
|
/* calculate confirmation */
|
|
err = smp_c1(smp->tk, smp->rrnd, smp->preq, smp->prsp,
|
|
&conn->le.init_addr, &conn->le.resp_addr, tmp);
|
|
if (err) {
|
|
return BT_SMP_ERR_UNSPECIFIED;
|
|
}
|
|
|
|
LOG_DBG("pcnf %s", bt_hex(smp->pcnf, 16));
|
|
LOG_DBG("cfm %s", bt_hex(tmp, 16));
|
|
|
|
if (memcmp(smp->pcnf, tmp, sizeof(smp->pcnf))) {
|
|
return BT_SMP_ERR_CONFIRM_FAILED;
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_CENTRAL) &&
|
|
conn->role == BT_HCI_ROLE_CENTRAL) {
|
|
uint8_t ediv[2], rand[8];
|
|
|
|
/* No need to store central STK */
|
|
err = smp_s1(smp->tk, smp->rrnd, smp->prnd, tmp);
|
|
if (err) {
|
|
return BT_SMP_ERR_UNSPECIFIED;
|
|
}
|
|
|
|
/* Rand and EDiv are 0 for the STK */
|
|
(void)memset(ediv, 0, sizeof(ediv));
|
|
(void)memset(rand, 0, sizeof(rand));
|
|
if (bt_conn_le_start_encryption(conn, rand, ediv, tmp,
|
|
get_encryption_key_size(smp))) {
|
|
LOG_ERR("Failed to start encryption");
|
|
return BT_SMP_ERR_UNSPECIFIED;
|
|
}
|
|
|
|
atomic_set_bit(smp->flags, SMP_FLAG_ENC_PENDING);
|
|
|
|
if (IS_ENABLED(CONFIG_BT_SMP_USB_HCI_CTLR_WORKAROUND)) {
|
|
if (smp->remote_dist & BT_SMP_DIST_ENC_KEY) {
|
|
atomic_set_bit(smp->allowed_cmds,
|
|
BT_SMP_CMD_ENCRYPT_INFO);
|
|
} else if (smp->remote_dist & BT_SMP_DIST_ID_KEY) {
|
|
atomic_set_bit(smp->allowed_cmds,
|
|
BT_SMP_CMD_IDENT_INFO);
|
|
} else if (smp->remote_dist & BT_SMP_DIST_SIGN) {
|
|
atomic_set_bit(smp->allowed_cmds,
|
|
BT_SMP_CMD_SIGNING_INFO);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_PERIPHERAL)) {
|
|
err = smp_s1(smp->tk, smp->prnd, smp->rrnd, tmp);
|
|
if (err) {
|
|
LOG_ERR("Calculate STK failed");
|
|
return BT_SMP_ERR_UNSPECIFIED;
|
|
}
|
|
|
|
memcpy(smp->tk, tmp, sizeof(smp->tk));
|
|
LOG_DBG("generated STK %s", bt_hex(smp->tk, 16));
|
|
|
|
atomic_set_bit(smp->flags, SMP_FLAG_ENC_PENDING);
|
|
|
|
return smp_send_pairing_random(smp);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static uint8_t legacy_pairing_confirm(struct bt_smp *smp)
|
|
{
|
|
LOG_DBG("");
|
|
|
|
if (IS_ENABLED(CONFIG_BT_CENTRAL) &&
|
|
smp->chan.chan.conn->role == BT_HCI_ROLE_CENTRAL) {
|
|
atomic_set_bit(smp->allowed_cmds, BT_SMP_CMD_PAIRING_CONFIRM);
|
|
return legacy_send_pairing_confirm(smp);
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_PERIPHERAL)) {
|
|
if (!atomic_test_bit(smp->flags, SMP_FLAG_USER)) {
|
|
atomic_set_bit(smp->allowed_cmds,
|
|
BT_SMP_CMD_PAIRING_RANDOM);
|
|
return legacy_send_pairing_confirm(smp);
|
|
}
|
|
|
|
atomic_set_bit(smp->flags, SMP_FLAG_CFM_DELAYED);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void legacy_user_tk_entry(struct bt_smp *smp)
|
|
{
|
|
if (!atomic_test_and_clear_bit(smp->flags, SMP_FLAG_CFM_DELAYED)) {
|
|
return;
|
|
}
|
|
|
|
/* if confirm failed ie. due to invalid passkey, cancel pairing */
|
|
if (legacy_pairing_confirm(smp)) {
|
|
smp_error(smp, BT_SMP_ERR_PASSKEY_ENTRY_FAILED);
|
|
return;
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_CENTRAL) &&
|
|
smp->chan.chan.conn->role == BT_HCI_ROLE_CENTRAL) {
|
|
atomic_set_bit(smp->allowed_cmds, BT_SMP_CMD_PAIRING_CONFIRM);
|
|
return;
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_PERIPHERAL)) {
|
|
atomic_set_bit(smp->allowed_cmds, BT_SMP_CMD_PAIRING_RANDOM);
|
|
}
|
|
}
|
|
|
|
static void legacy_passkey_entry(struct bt_smp *smp, unsigned int passkey)
|
|
{
|
|
passkey = sys_cpu_to_le32(passkey);
|
|
memcpy(smp->tk, &passkey, sizeof(passkey));
|
|
|
|
legacy_user_tk_entry(smp);
|
|
}
|
|
|
|
static uint8_t smp_encrypt_info(struct bt_smp *smp, struct net_buf *buf)
|
|
{
|
|
LOG_DBG("");
|
|
|
|
if (atomic_test_bit(smp->flags, SMP_FLAG_BOND)) {
|
|
struct bt_smp_encrypt_info *req = (void *)buf->data;
|
|
struct bt_conn *conn = smp->chan.chan.conn;
|
|
struct bt_keys *keys;
|
|
|
|
keys = bt_keys_get_type(BT_KEYS_LTK, conn->id, &conn->le.dst);
|
|
if (!keys) {
|
|
LOG_ERR("Unable to get keys for %s", bt_addr_le_str(&conn->le.dst));
|
|
return BT_SMP_ERR_UNSPECIFIED;
|
|
}
|
|
|
|
memcpy(keys->ltk.val, req->ltk, 16);
|
|
}
|
|
|
|
atomic_set_bit(smp->allowed_cmds, BT_SMP_CMD_CENTRAL_IDENT);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static uint8_t smp_central_ident(struct bt_smp *smp, struct net_buf *buf)
|
|
{
|
|
struct bt_conn *conn = smp->chan.chan.conn;
|
|
uint8_t err;
|
|
|
|
LOG_DBG("");
|
|
|
|
if (atomic_test_bit(smp->flags, SMP_FLAG_BOND)) {
|
|
struct bt_smp_central_ident *req = (void *)buf->data;
|
|
struct bt_keys *keys;
|
|
|
|
keys = bt_keys_get_type(BT_KEYS_LTK, conn->id, &conn->le.dst);
|
|
if (!keys) {
|
|
LOG_ERR("Unable to get keys for %s", bt_addr_le_str(&conn->le.dst));
|
|
return BT_SMP_ERR_UNSPECIFIED;
|
|
}
|
|
|
|
memcpy(keys->ltk.ediv, req->ediv, sizeof(keys->ltk.ediv));
|
|
memcpy(keys->ltk.rand, req->rand, sizeof(req->rand));
|
|
}
|
|
|
|
smp->remote_dist &= ~BT_SMP_DIST_ENC_KEY;
|
|
|
|
if (smp->remote_dist & BT_SMP_DIST_ID_KEY) {
|
|
atomic_set_bit(smp->allowed_cmds, BT_SMP_CMD_IDENT_INFO);
|
|
} else if (smp->remote_dist & BT_SMP_DIST_SIGN) {
|
|
atomic_set_bit(smp->allowed_cmds, BT_SMP_CMD_SIGNING_INFO);
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_CENTRAL) &&
|
|
conn->role == BT_HCI_ROLE_CENTRAL && !smp->remote_dist) {
|
|
err = bt_smp_distribute_keys(smp);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
}
|
|
|
|
/* if all keys were distributed, pairing is done */
|
|
if (!smp->local_dist && !smp->remote_dist) {
|
|
smp_pairing_complete(smp, 0);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if defined(CONFIG_BT_CENTRAL)
|
|
static uint8_t legacy_pairing_rsp(struct bt_smp *smp)
|
|
{
|
|
const struct bt_conn_auth_cb *smp_auth_cb = latch_auth_cb(smp);
|
|
uint8_t ret;
|
|
|
|
LOG_DBG("");
|
|
|
|
ret = legacy_request_tk(smp);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
|
|
/* ask for consent if this is due to received SecReq */
|
|
if ((DISPLAY_FIXED(smp) || smp->method == JUST_WORKS) &&
|
|
atomic_test_bit(smp->flags, SMP_FLAG_SEC_REQ) &&
|
|
smp_auth_cb && smp_auth_cb->pairing_confirm) {
|
|
atomic_set_bit(smp->flags, SMP_FLAG_USER);
|
|
smp_auth_cb->pairing_confirm(smp->chan.chan.conn);
|
|
return 0;
|
|
}
|
|
|
|
if (!atomic_test_bit(smp->flags, SMP_FLAG_USER)) {
|
|
atomic_set_bit(smp->allowed_cmds, BT_SMP_CMD_PAIRING_CONFIRM);
|
|
atomic_set_bit(smp->allowed_cmds, BT_SMP_KEYPRESS_NOTIFICATION);
|
|
return legacy_send_pairing_confirm(smp);
|
|
}
|
|
|
|
atomic_set_bit(smp->flags, SMP_FLAG_CFM_DELAYED);
|
|
|
|
return 0;
|
|
}
|
|
#endif /* CONFIG_BT_CENTRAL */
|
|
#else
|
|
static uint8_t smp_encrypt_info(struct bt_smp *smp, struct net_buf *buf)
|
|
{
|
|
return BT_SMP_ERR_CMD_NOTSUPP;
|
|
}
|
|
|
|
static uint8_t smp_central_ident(struct bt_smp *smp, struct net_buf *buf)
|
|
{
|
|
return BT_SMP_ERR_CMD_NOTSUPP;
|
|
}
|
|
#endif /* !CONFIG_BT_SMP_SC_PAIR_ONLY */
|
|
|
|
static int smp_init(struct bt_smp *smp)
|
|
{
|
|
/* Initialize SMP context exluding L2CAP channel context and anything
|
|
* else declared after.
|
|
*/
|
|
(void)memset(smp, 0, offsetof(struct bt_smp, chan));
|
|
|
|
/* Generate local random number */
|
|
if (bt_rand(smp->prnd, 16)) {
|
|
return BT_SMP_ERR_UNSPECIFIED;
|
|
}
|
|
|
|
LOG_DBG("prnd %s", bt_hex(smp->prnd, 16));
|
|
|
|
atomic_set_bit(smp->allowed_cmds, BT_SMP_CMD_PAIRING_FAIL);
|
|
|
|
#if !defined(CONFIG_BT_SMP_OOB_LEGACY_PAIR_ONLY)
|
|
sc_public_key = bt_pub_key_get();
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
void bt_set_bondable(bool enable)
|
|
{
|
|
bondable = enable;
|
|
}
|
|
|
|
void bt_le_oob_set_sc_flag(bool enable)
|
|
{
|
|
sc_oobd_present = enable;
|
|
}
|
|
|
|
void bt_le_oob_set_legacy_flag(bool enable)
|
|
{
|
|
legacy_oobd_present = enable;
|
|
}
|
|
|
|
static uint8_t get_auth(struct bt_smp *smp, uint8_t auth)
|
|
{
|
|
struct bt_conn *conn = smp->chan.chan.conn;
|
|
|
|
if (sc_supported) {
|
|
auth &= BT_SMP_AUTH_MASK_SC;
|
|
} else {
|
|
auth &= BT_SMP_AUTH_MASK;
|
|
}
|
|
|
|
if ((get_io_capa(smp) == BT_SMP_IO_NO_INPUT_OUTPUT) ||
|
|
(!IS_ENABLED(CONFIG_BT_SMP_ENFORCE_MITM) &&
|
|
(conn->required_sec_level < BT_SECURITY_L3))) {
|
|
auth &= ~(BT_SMP_AUTH_MITM);
|
|
} else {
|
|
auth |= BT_SMP_AUTH_MITM;
|
|
}
|
|
|
|
if (latch_bondable(smp)) {
|
|
auth |= BT_SMP_AUTH_BONDING;
|
|
} else {
|
|
auth &= ~BT_SMP_AUTH_BONDING;
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_PASSKEY_KEYPRESS)) {
|
|
auth |= BT_SMP_AUTH_KEYPRESS;
|
|
} else {
|
|
auth &= ~BT_SMP_AUTH_KEYPRESS;
|
|
}
|
|
|
|
return auth;
|
|
}
|
|
|
|
static uint8_t remote_sec_level_reachable(struct bt_smp *smp)
|
|
{
|
|
bt_security_t sec = smp->chan.chan.conn->required_sec_level;
|
|
|
|
if (IS_ENABLED(CONFIG_BT_SMP_SC_ONLY)) {
|
|
sec = BT_SECURITY_L4;
|
|
}
|
|
if (IS_ENABLED(CONFIG_BT_SMP_OOB_LEGACY_PAIR_ONLY)) {
|
|
sec = BT_SECURITY_L3;
|
|
}
|
|
|
|
switch (sec) {
|
|
case BT_SECURITY_L1:
|
|
case BT_SECURITY_L2:
|
|
return 0;
|
|
|
|
case BT_SECURITY_L4:
|
|
if (get_encryption_key_size(smp) != BT_SMP_MAX_ENC_KEY_SIZE) {
|
|
return BT_SMP_ERR_ENC_KEY_SIZE;
|
|
}
|
|
|
|
if (!atomic_test_bit(smp->flags, SMP_FLAG_SC)) {
|
|
return BT_SMP_ERR_AUTH_REQUIREMENTS;
|
|
}
|
|
__fallthrough;
|
|
case BT_SECURITY_L3:
|
|
if (smp->method == JUST_WORKS) {
|
|
return BT_SMP_ERR_AUTH_REQUIREMENTS;
|
|
}
|
|
|
|
return 0;
|
|
default:
|
|
return BT_SMP_ERR_UNSPECIFIED;
|
|
}
|
|
}
|
|
|
|
static bool sec_level_reachable(struct bt_smp *smp)
|
|
{
|
|
const struct bt_conn_auth_cb *smp_auth_cb = latch_auth_cb(smp);
|
|
|
|
switch (smp->chan.chan.conn->required_sec_level) {
|
|
case BT_SECURITY_L1:
|
|
case BT_SECURITY_L2:
|
|
return true;
|
|
case BT_SECURITY_L3:
|
|
return get_io_capa(smp) != BT_SMP_IO_NO_INPUT_OUTPUT ||
|
|
(smp_auth_cb && smp_auth_cb->oob_data_request);
|
|
case BT_SECURITY_L4:
|
|
return (get_io_capa(smp) != BT_SMP_IO_NO_INPUT_OUTPUT ||
|
|
(smp_auth_cb && smp_auth_cb->oob_data_request)) && sc_supported;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static struct bt_smp *smp_chan_get(struct bt_conn *conn)
|
|
{
|
|
struct bt_l2cap_chan *chan;
|
|
|
|
chan = bt_l2cap_le_lookup_rx_cid(conn, BT_L2CAP_CID_SMP);
|
|
if (!chan) {
|
|
LOG_ERR("Unable to find SMP channel");
|
|
return NULL;
|
|
}
|
|
|
|
return CONTAINER_OF(chan, struct bt_smp, chan.chan);
|
|
}
|
|
|
|
bool bt_smp_request_ltk(struct bt_conn *conn, uint64_t rand, uint16_t ediv, uint8_t *ltk)
|
|
{
|
|
struct bt_smp *smp;
|
|
uint8_t enc_size;
|
|
|
|
smp = smp_chan_get(conn);
|
|
if (!smp) {
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* Both legacy STK and LE SC LTK have rand and ediv equal to zero.
|
|
* If pairing is in progress use the TK for encryption.
|
|
*/
|
|
if (ediv == 0U && rand == 0U &&
|
|
atomic_test_bit(smp->flags, SMP_FLAG_PAIRING) &&
|
|
atomic_test_bit(smp->flags, SMP_FLAG_ENC_PENDING)) {
|
|
enc_size = get_encryption_key_size(smp);
|
|
|
|
/*
|
|
* We keep both legacy STK and LE SC LTK in TK.
|
|
* Also use only enc_size bytes of key for encryption.
|
|
*/
|
|
memcpy(ltk, smp->tk, enc_size);
|
|
if (enc_size < BT_SMP_MAX_ENC_KEY_SIZE) {
|
|
(void)memset(ltk + enc_size, 0,
|
|
BT_SMP_MAX_ENC_KEY_SIZE - enc_size);
|
|
}
|
|
|
|
atomic_set_bit(smp->flags, SMP_FLAG_ENC_PENDING);
|
|
return true;
|
|
}
|
|
|
|
if (!conn->le.keys) {
|
|
conn->le.keys = bt_keys_find(BT_KEYS_LTK_P256, conn->id,
|
|
&conn->le.dst);
|
|
if (!conn->le.keys) {
|
|
conn->le.keys = bt_keys_find(BT_KEYS_PERIPH_LTK,
|
|
conn->id, &conn->le.dst);
|
|
}
|
|
}
|
|
|
|
if (ediv == 0U && rand == 0U &&
|
|
conn->le.keys && (conn->le.keys->keys & BT_KEYS_LTK_P256)) {
|
|
enc_size = conn->le.keys->enc_size;
|
|
|
|
memcpy(ltk, conn->le.keys->ltk.val, enc_size);
|
|
if (enc_size < BT_SMP_MAX_ENC_KEY_SIZE) {
|
|
(void)memset(ltk + enc_size, 0,
|
|
BT_SMP_MAX_ENC_KEY_SIZE - enc_size);
|
|
}
|
|
|
|
atomic_set_bit(smp->flags, SMP_FLAG_ENC_PENDING);
|
|
return true;
|
|
}
|
|
|
|
#if !defined(CONFIG_BT_SMP_SC_PAIR_ONLY)
|
|
if (conn->le.keys && (conn->le.keys->keys & BT_KEYS_PERIPH_LTK) &&
|
|
!memcmp(conn->le.keys->periph_ltk.rand, &rand, 8) &&
|
|
!memcmp(conn->le.keys->periph_ltk.ediv, &ediv, 2)) {
|
|
enc_size = conn->le.keys->enc_size;
|
|
|
|
memcpy(ltk, conn->le.keys->periph_ltk.val, enc_size);
|
|
if (enc_size < BT_SMP_MAX_ENC_KEY_SIZE) {
|
|
(void)memset(ltk + enc_size, 0,
|
|
BT_SMP_MAX_ENC_KEY_SIZE - enc_size);
|
|
}
|
|
|
|
atomic_set_bit(smp->flags, SMP_FLAG_ENC_PENDING);
|
|
return true;
|
|
}
|
|
#endif /* !CONFIG_BT_SMP_SC_PAIR_ONLY */
|
|
|
|
if (atomic_test_bit(smp->flags, SMP_FLAG_SEC_REQ)) {
|
|
/* Notify higher level that security failed if security was
|
|
* initiated by peripheral.
|
|
*/
|
|
bt_conn_security_changed(conn, BT_HCI_ERR_PIN_OR_KEY_MISSING,
|
|
BT_SECURITY_ERR_PIN_OR_KEY_MISSING);
|
|
}
|
|
|
|
smp_reset(smp);
|
|
return false;
|
|
}
|
|
|
|
#if defined(CONFIG_BT_PERIPHERAL)
|
|
static int smp_send_security_req(struct bt_conn *conn)
|
|
{
|
|
struct bt_smp *smp;
|
|
struct bt_smp_security_request *req;
|
|
struct net_buf *req_buf;
|
|
int err;
|
|
|
|
LOG_DBG("");
|
|
smp = smp_chan_get(conn);
|
|
if (!smp) {
|
|
return -ENOTCONN;
|
|
}
|
|
|
|
/* SMP Timeout */
|
|
if (atomic_test_bit(smp->flags, SMP_FLAG_TIMEOUT)) {
|
|
return -EIO;
|
|
}
|
|
|
|
/* pairing is in progress */
|
|
if (atomic_test_bit(smp->flags, SMP_FLAG_PAIRING)) {
|
|
return -EBUSY;
|
|
}
|
|
|
|
if (atomic_test_bit(smp->flags, SMP_FLAG_ENC_PENDING)) {
|
|
return -EBUSY;
|
|
}
|
|
|
|
/* early verify if required sec level if reachable */
|
|
if (!(sec_level_reachable(smp) || smp_keys_check(conn))) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!conn->le.keys) {
|
|
conn->le.keys = bt_keys_get_addr(conn->id, &conn->le.dst);
|
|
if (!conn->le.keys) {
|
|
return -ENOMEM;
|
|
}
|
|
}
|
|
|
|
if (smp_init(smp) != 0) {
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
req_buf = smp_create_pdu(smp, BT_SMP_CMD_SECURITY_REQUEST,
|
|
sizeof(*req));
|
|
if (!req_buf) {
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
req = net_buf_add(req_buf, sizeof(*req));
|
|
req->auth_req = get_auth(smp, BT_SMP_AUTH_DEFAULT);
|
|
|
|
/* SMP timer is not restarted for SecRequest so don't use smp_send */
|
|
err = bt_l2cap_send(conn, BT_L2CAP_CID_SMP, req_buf);
|
|
if (err) {
|
|
net_buf_unref(req_buf);
|
|
return err;
|
|
}
|
|
|
|
atomic_set_bit(smp->flags, SMP_FLAG_SEC_REQ);
|
|
atomic_set_bit(smp->allowed_cmds, BT_SMP_CMD_PAIRING_REQ);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static uint8_t smp_pairing_req(struct bt_smp *smp, struct net_buf *buf)
|
|
{
|
|
struct bt_conn *conn = smp->chan.chan.conn;
|
|
const struct bt_conn_auth_cb *smp_auth_cb = latch_auth_cb(smp);
|
|
|
|
struct bt_smp_pairing *req = (void *)buf->data;
|
|
struct bt_smp_pairing *rsp;
|
|
uint8_t err;
|
|
|
|
LOG_DBG("req: io_capability 0x%02X, oob_flag 0x%02X, auth_req 0x%02X, "
|
|
"max_key_size 0x%02X, init_key_dist 0x%02X, resp_key_dist 0x%02X",
|
|
req->io_capability, req->oob_flag, req->auth_req,
|
|
req->max_key_size, req->init_key_dist, req->resp_key_dist);
|
|
|
|
if ((req->max_key_size > BT_SMP_MAX_ENC_KEY_SIZE) ||
|
|
(req->max_key_size < BT_SMP_MIN_ENC_KEY_SIZE)) {
|
|
return BT_SMP_ERR_ENC_KEY_SIZE;
|
|
}
|
|
|
|
if (!conn->le.keys) {
|
|
conn->le.keys = bt_keys_get_addr(conn->id, &conn->le.dst);
|
|
if (!conn->le.keys) {
|
|
LOG_DBG("Unable to get keys for %s", bt_addr_le_str(&conn->le.dst));
|
|
return BT_SMP_ERR_UNSPECIFIED;
|
|
}
|
|
}
|
|
|
|
/* If we already sent a security request then the SMP context
|
|
* is already initialized.
|
|
*/
|
|
if (!atomic_test_bit(smp->flags, SMP_FLAG_SEC_REQ)) {
|
|
int ret = smp_init(smp);
|
|
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
/* Store req for later use */
|
|
smp->preq[0] = BT_SMP_CMD_PAIRING_REQ;
|
|
memcpy(smp->preq + 1, req, sizeof(*req));
|
|
|
|
/* create rsp, it will be used later on */
|
|
smp->prsp[0] = BT_SMP_CMD_PAIRING_RSP;
|
|
rsp = (struct bt_smp_pairing *)&smp->prsp[1];
|
|
|
|
rsp->auth_req = get_auth(smp, req->auth_req);
|
|
rsp->io_capability = get_io_capa(smp);
|
|
rsp->max_key_size = BT_SMP_MAX_ENC_KEY_SIZE;
|
|
rsp->init_key_dist = (req->init_key_dist & RECV_KEYS);
|
|
rsp->resp_key_dist = (req->resp_key_dist & SEND_KEYS);
|
|
|
|
if ((rsp->auth_req & BT_SMP_AUTH_SC) &&
|
|
(req->auth_req & BT_SMP_AUTH_SC)) {
|
|
atomic_set_bit(smp->flags, SMP_FLAG_SC);
|
|
|
|
rsp->init_key_dist &= RECV_KEYS_SC;
|
|
rsp->resp_key_dist &= SEND_KEYS_SC;
|
|
}
|
|
|
|
if (atomic_test_bit(smp->flags, SMP_FLAG_SC)) {
|
|
rsp->oob_flag = sc_oobd_present ? BT_SMP_OOB_PRESENT :
|
|
BT_SMP_OOB_NOT_PRESENT;
|
|
} else {
|
|
rsp->oob_flag = legacy_oobd_present ? BT_SMP_OOB_PRESENT :
|
|
BT_SMP_OOB_NOT_PRESENT;
|
|
}
|
|
|
|
if ((rsp->auth_req & BT_SMP_AUTH_CT2) &&
|
|
(req->auth_req & BT_SMP_AUTH_CT2)) {
|
|
atomic_set_bit(smp->flags, SMP_FLAG_CT2);
|
|
}
|
|
|
|
if ((rsp->auth_req & BT_SMP_AUTH_BONDING) &&
|
|
(req->auth_req & BT_SMP_AUTH_BONDING)) {
|
|
atomic_set_bit(smp->flags, SMP_FLAG_BOND);
|
|
} else if (IS_ENABLED(CONFIG_BT_BONDING_REQUIRED)) {
|
|
/* Reject pairing req if not both intend to bond */
|
|
LOG_DBG("Bonding required");
|
|
return BT_SMP_ERR_UNSPECIFIED;
|
|
} else {
|
|
rsp->init_key_dist = 0;
|
|
rsp->resp_key_dist = 0;
|
|
}
|
|
|
|
smp->local_dist = rsp->resp_key_dist;
|
|
smp->remote_dist = rsp->init_key_dist;
|
|
|
|
atomic_set_bit(smp->flags, SMP_FLAG_PAIRING);
|
|
|
|
smp->method = get_pair_method(smp, req->io_capability);
|
|
|
|
if (!update_keys_check(smp, conn->le.keys)) {
|
|
return BT_SMP_ERR_AUTH_REQUIREMENTS;
|
|
}
|
|
|
|
err = remote_sec_level_reachable(smp);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
if (!atomic_test_bit(smp->flags, SMP_FLAG_SC)) {
|
|
#if defined(CONFIG_BT_SMP_SC_PAIR_ONLY)
|
|
return BT_SMP_ERR_AUTH_REQUIREMENTS;
|
|
#else
|
|
if (IS_ENABLED(CONFIG_BT_SMP_APP_PAIRING_ACCEPT)) {
|
|
err = smp_pairing_accept_query(smp, req);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
}
|
|
|
|
return legacy_pairing_req(smp);
|
|
#endif /* CONFIG_BT_SMP_SC_PAIR_ONLY */
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_SMP_APP_PAIRING_ACCEPT)) {
|
|
err = smp_pairing_accept_query(smp, req);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
}
|
|
|
|
if (!IS_ENABLED(CONFIG_BT_SMP_SC_PAIR_ONLY) &&
|
|
(DISPLAY_FIXED(smp) || smp->method == JUST_WORKS) &&
|
|
!atomic_test_bit(smp->flags, SMP_FLAG_SEC_REQ) &&
|
|
smp_auth_cb && smp_auth_cb->pairing_confirm) {
|
|
atomic_set_bit(smp->flags, SMP_FLAG_USER);
|
|
smp_auth_cb->pairing_confirm(conn);
|
|
return 0;
|
|
}
|
|
|
|
atomic_set_bit(smp->allowed_cmds, BT_SMP_CMD_PUBLIC_KEY);
|
|
|
|
LOG_DBG("rsp: io_capability 0x%02X, oob_flag 0x%02X, auth_req 0x%02X, "
|
|
"max_key_size 0x%02X, init_key_dist 0x%02X, resp_key_dist 0x%02X",
|
|
rsp->io_capability, rsp->oob_flag, rsp->auth_req,
|
|
rsp->max_key_size, rsp->init_key_dist, rsp->resp_key_dist);
|
|
|
|
return send_pairing_rsp(smp);
|
|
}
|
|
#else
|
|
static uint8_t smp_pairing_req(struct bt_smp *smp, struct net_buf *buf)
|
|
{
|
|
return BT_SMP_ERR_CMD_NOTSUPP;
|
|
}
|
|
#endif /* CONFIG_BT_PERIPHERAL */
|
|
|
|
static uint8_t sc_send_public_key(struct bt_smp *smp)
|
|
{
|
|
struct bt_smp_public_key *req;
|
|
struct net_buf *req_buf;
|
|
|
|
req_buf = smp_create_pdu(smp, BT_SMP_CMD_PUBLIC_KEY, sizeof(*req));
|
|
if (!req_buf) {
|
|
return BT_SMP_ERR_UNSPECIFIED;
|
|
}
|
|
|
|
req = net_buf_add(req_buf, sizeof(*req));
|
|
|
|
memcpy(req->x, sc_public_key, sizeof(req->x));
|
|
memcpy(req->y, &sc_public_key[32], sizeof(req->y));
|
|
|
|
smp_send(smp, req_buf, NULL, NULL);
|
|
|
|
if (IS_ENABLED(CONFIG_BT_USE_DEBUG_KEYS)) {
|
|
atomic_set_bit(smp->flags, SMP_FLAG_SC_DEBUG_KEY);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if defined(CONFIG_BT_CENTRAL)
|
|
static int smp_send_pairing_req(struct bt_conn *conn)
|
|
{
|
|
struct bt_smp *smp;
|
|
struct bt_smp_pairing *req;
|
|
struct net_buf *req_buf;
|
|
|
|
LOG_DBG("");
|
|
|
|
smp = smp_chan_get(conn);
|
|
if (!smp) {
|
|
return -ENOTCONN;
|
|
}
|
|
|
|
/* SMP Timeout */
|
|
if (atomic_test_bit(smp->flags, SMP_FLAG_TIMEOUT)) {
|
|
return -EIO;
|
|
}
|
|
|
|
/* A higher security level is requested during the key distribution
|
|
* phase, once pairing is complete a new pairing procedure will start.
|
|
*/
|
|
if (atomic_test_bit(smp->flags, SMP_FLAG_KEYS_DISTR)) {
|
|
return 0;
|
|
}
|
|
|
|
/* pairing is in progress */
|
|
if (atomic_test_bit(smp->flags, SMP_FLAG_PAIRING)) {
|
|
return -EBUSY;
|
|
}
|
|
|
|
/* Encryption is in progress */
|
|
if (atomic_test_bit(smp->flags, SMP_FLAG_ENC_PENDING)) {
|
|
return -EBUSY;
|
|
}
|
|
|
|
/* early verify if required sec level if reachable */
|
|
if (!sec_level_reachable(smp)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!conn->le.keys) {
|
|
conn->le.keys = bt_keys_get_addr(conn->id, &conn->le.dst);
|
|
if (!conn->le.keys) {
|
|
return -ENOMEM;
|
|
}
|
|
}
|
|
|
|
if (smp_init(smp)) {
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
req_buf = smp_create_pdu(smp, BT_SMP_CMD_PAIRING_REQ, sizeof(*req));
|
|
if (!req_buf) {
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
req = net_buf_add(req_buf, sizeof(*req));
|
|
|
|
req->auth_req = get_auth(smp, BT_SMP_AUTH_DEFAULT);
|
|
req->io_capability = get_io_capa(smp);
|
|
|
|
/* At this point is it unknown if pairing will be legacy or LE SC so
|
|
* set OOB flag if any OOB data is present and assume to peer device
|
|
* provides OOB data that will match it's pairing type.
|
|
*/
|
|
req->oob_flag = (legacy_oobd_present || sc_oobd_present) ?
|
|
BT_SMP_OOB_PRESENT : BT_SMP_OOB_NOT_PRESENT;
|
|
|
|
req->max_key_size = BT_SMP_MAX_ENC_KEY_SIZE;
|
|
|
|
if (req->auth_req & BT_SMP_AUTH_BONDING) {
|
|
req->init_key_dist = SEND_KEYS;
|
|
req->resp_key_dist = RECV_KEYS;
|
|
} else {
|
|
req->init_key_dist = 0;
|
|
req->resp_key_dist = 0;
|
|
}
|
|
|
|
smp->local_dist = req->init_key_dist;
|
|
smp->remote_dist = req->resp_key_dist;
|
|
|
|
/* Store req for later use */
|
|
smp->preq[0] = BT_SMP_CMD_PAIRING_REQ;
|
|
memcpy(smp->preq + 1, req, sizeof(*req));
|
|
|
|
LOG_DBG("req: io_capability 0x%02X, oob_flag 0x%02X, auth_req 0x%02X, "
|
|
"max_key_size 0x%02X, init_key_dist 0x%02X, resp_key_dist 0x%02X",
|
|
req->io_capability, req->oob_flag, req->auth_req,
|
|
req->max_key_size, req->init_key_dist, req->resp_key_dist);
|
|
|
|
smp_send(smp, req_buf, NULL, NULL);
|
|
|
|
atomic_set_bit(smp->allowed_cmds, BT_SMP_CMD_PAIRING_RSP);
|
|
atomic_set_bit(smp->allowed_cmds, BT_SMP_CMD_SECURITY_REQUEST);
|
|
atomic_set_bit(smp->flags, SMP_FLAG_PAIRING);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static uint8_t smp_pairing_rsp(struct bt_smp *smp, struct net_buf *buf)
|
|
{
|
|
struct bt_conn *conn = smp->chan.chan.conn;
|
|
const struct bt_conn_auth_cb *smp_auth_cb = latch_auth_cb(smp);
|
|
struct bt_smp_pairing *rsp = (void *)buf->data;
|
|
struct bt_smp_pairing *req = (struct bt_smp_pairing *)&smp->preq[1];
|
|
uint8_t err;
|
|
|
|
LOG_DBG("rsp: io_capability 0x%02X, oob_flag 0x%02X, auth_req 0x%02X, "
|
|
"max_key_size 0x%02X, init_key_dist 0x%02X, resp_key_dist 0x%02X",
|
|
rsp->io_capability, rsp->oob_flag, rsp->auth_req,
|
|
rsp->max_key_size, rsp->init_key_dist, rsp->resp_key_dist);
|
|
|
|
if ((rsp->max_key_size > BT_SMP_MAX_ENC_KEY_SIZE) ||
|
|
(rsp->max_key_size < BT_SMP_MIN_ENC_KEY_SIZE)) {
|
|
return BT_SMP_ERR_ENC_KEY_SIZE;
|
|
}
|
|
|
|
smp->local_dist &= rsp->init_key_dist;
|
|
smp->remote_dist &= rsp->resp_key_dist;
|
|
|
|
/* Store rsp for later use */
|
|
smp->prsp[0] = BT_SMP_CMD_PAIRING_RSP;
|
|
memcpy(smp->prsp + 1, rsp, sizeof(*rsp));
|
|
|
|
if ((rsp->auth_req & BT_SMP_AUTH_SC) &&
|
|
(req->auth_req & BT_SMP_AUTH_SC)) {
|
|
atomic_set_bit(smp->flags, SMP_FLAG_SC);
|
|
}
|
|
|
|
if ((rsp->auth_req & BT_SMP_AUTH_CT2) &&
|
|
(req->auth_req & BT_SMP_AUTH_CT2)) {
|
|
atomic_set_bit(smp->flags, SMP_FLAG_CT2);
|
|
}
|
|
|
|
if ((rsp->auth_req & BT_SMP_AUTH_BONDING) &&
|
|
(req->auth_req & BT_SMP_AUTH_BONDING)) {
|
|
atomic_set_bit(smp->flags, SMP_FLAG_BOND);
|
|
} else if (IS_ENABLED(CONFIG_BT_BONDING_REQUIRED)) {
|
|
/* Reject pairing req if not both intend to bond */
|
|
LOG_DBG("Bonding required");
|
|
return BT_SMP_ERR_UNSPECIFIED;
|
|
} else {
|
|
smp->local_dist = 0;
|
|
smp->remote_dist = 0;
|
|
}
|
|
|
|
smp->method = get_pair_method(smp, rsp->io_capability);
|
|
|
|
if (!update_keys_check(smp, conn->le.keys)) {
|
|
return BT_SMP_ERR_AUTH_REQUIREMENTS;
|
|
}
|
|
|
|
err = remote_sec_level_reachable(smp);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
if (!atomic_test_bit(smp->flags, SMP_FLAG_SC)) {
|
|
#if defined(CONFIG_BT_SMP_SC_PAIR_ONLY)
|
|
return BT_SMP_ERR_AUTH_REQUIREMENTS;
|
|
#else
|
|
if (IS_ENABLED(CONFIG_BT_SMP_APP_PAIRING_ACCEPT)) {
|
|
err = smp_pairing_accept_query(smp, rsp);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
}
|
|
|
|
return legacy_pairing_rsp(smp);
|
|
#endif /* CONFIG_BT_SMP_SC_PAIR_ONLY */
|
|
}
|
|
|
|
smp->local_dist &= SEND_KEYS_SC;
|
|
smp->remote_dist &= RECV_KEYS_SC;
|
|
|
|
if (IS_ENABLED(CONFIG_BT_SMP_APP_PAIRING_ACCEPT)) {
|
|
err = smp_pairing_accept_query(smp, rsp);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
}
|
|
|
|
if (!IS_ENABLED(CONFIG_BT_SMP_SC_PAIR_ONLY) &&
|
|
(DISPLAY_FIXED(smp) || smp->method == JUST_WORKS) &&
|
|
atomic_test_bit(smp->flags, SMP_FLAG_SEC_REQ) &&
|
|
smp_auth_cb && smp_auth_cb->pairing_confirm) {
|
|
atomic_set_bit(smp->flags, SMP_FLAG_USER);
|
|
smp_auth_cb->pairing_confirm(conn);
|
|
return 0;
|
|
}
|
|
|
|
if (!sc_public_key) {
|
|
atomic_set_bit(smp->flags, SMP_FLAG_PKEY_SEND);
|
|
return 0;
|
|
}
|
|
|
|
atomic_set_bit(smp->allowed_cmds, BT_SMP_CMD_PUBLIC_KEY);
|
|
atomic_clear_bit(smp->allowed_cmds, BT_SMP_CMD_SECURITY_REQUEST);
|
|
|
|
return sc_send_public_key(smp);
|
|
}
|
|
#else
|
|
static uint8_t smp_pairing_rsp(struct bt_smp *smp, struct net_buf *buf)
|
|
{
|
|
return BT_SMP_ERR_CMD_NOTSUPP;
|
|
}
|
|
#endif /* CONFIG_BT_CENTRAL */
|
|
|
|
static uint8_t smp_pairing_confirm(struct bt_smp *smp, struct net_buf *buf)
|
|
{
|
|
struct bt_smp_pairing_confirm *req = (void *)buf->data;
|
|
|
|
LOG_DBG("");
|
|
|
|
atomic_clear_bit(smp->flags, SMP_FLAG_DISPLAY);
|
|
|
|
memcpy(smp->pcnf, req->val, sizeof(smp->pcnf));
|
|
|
|
if (IS_ENABLED(CONFIG_BT_CENTRAL) &&
|
|
smp->chan.chan.conn->role == BT_HCI_ROLE_CENTRAL) {
|
|
atomic_set_bit(smp->allowed_cmds, BT_SMP_CMD_PAIRING_RANDOM);
|
|
return smp_send_pairing_random(smp);
|
|
}
|
|
|
|
if (!IS_ENABLED(CONFIG_BT_PERIPHERAL)) {
|
|
return 0;
|
|
}
|
|
|
|
#if !defined(CONFIG_BT_SMP_SC_PAIR_ONLY)
|
|
if (!atomic_test_bit(smp->flags, SMP_FLAG_SC)) {
|
|
return legacy_pairing_confirm(smp);
|
|
}
|
|
#endif /* !CONFIG_BT_SMP_SC_PAIR_ONLY */
|
|
|
|
switch (smp->method) {
|
|
case PASSKEY_DISPLAY:
|
|
atomic_set_bit(smp->allowed_cmds, BT_SMP_CMD_PAIRING_RANDOM);
|
|
return smp_send_pairing_confirm(smp);
|
|
case PASSKEY_INPUT:
|
|
if (atomic_test_bit(smp->flags, SMP_FLAG_USER)) {
|
|
atomic_set_bit(smp->flags, SMP_FLAG_CFM_DELAYED);
|
|
return 0;
|
|
}
|
|
|
|
atomic_set_bit(smp->allowed_cmds, BT_SMP_CMD_PAIRING_RANDOM);
|
|
return smp_send_pairing_confirm(smp);
|
|
case JUST_WORKS:
|
|
case PASSKEY_CONFIRM:
|
|
default:
|
|
LOG_ERR("Unknown pairing method (%u)", smp->method);
|
|
return BT_SMP_ERR_UNSPECIFIED;
|
|
}
|
|
}
|
|
|
|
static uint8_t sc_smp_send_dhkey_check(struct bt_smp *smp, const uint8_t *e)
|
|
{
|
|
struct bt_smp_dhkey_check *req;
|
|
struct net_buf *buf;
|
|
|
|
LOG_DBG("");
|
|
|
|
buf = smp_create_pdu(smp, BT_SMP_DHKEY_CHECK, sizeof(*req));
|
|
if (!buf) {
|
|
return BT_SMP_ERR_UNSPECIFIED;
|
|
}
|
|
|
|
req = net_buf_add(buf, sizeof(*req));
|
|
memcpy(req->e, e, sizeof(req->e));
|
|
|
|
smp_send(smp, buf, NULL, NULL);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if defined(CONFIG_BT_CENTRAL)
|
|
static uint8_t compute_and_send_central_dhcheck(struct bt_smp *smp)
|
|
{
|
|
uint8_t e[16], r[16];
|
|
|
|
(void)memset(r, 0, sizeof(r));
|
|
|
|
switch (smp->method) {
|
|
case JUST_WORKS:
|
|
case PASSKEY_CONFIRM:
|
|
break;
|
|
case PASSKEY_DISPLAY:
|
|
case PASSKEY_INPUT:
|
|
memcpy(r, &smp->passkey, sizeof(smp->passkey));
|
|
break;
|
|
case LE_SC_OOB:
|
|
if (smp->oobd_remote) {
|
|
memcpy(r, smp->oobd_remote->r, sizeof(r));
|
|
}
|
|
break;
|
|
default:
|
|
LOG_ERR("Unknown pairing method (%u)", smp->method);
|
|
return BT_SMP_ERR_UNSPECIFIED;
|
|
}
|
|
|
|
/* calculate LTK and mackey */
|
|
if (bt_crypto_f5(smp->dhkey, smp->prnd, smp->rrnd, &smp->chan.chan.conn->le.init_addr,
|
|
&smp->chan.chan.conn->le.resp_addr, smp->mackey, smp->tk)) {
|
|
LOG_ERR("Calculate LTK failed");
|
|
return BT_SMP_ERR_UNSPECIFIED;
|
|
}
|
|
/* calculate local DHKey check */
|
|
if (bt_crypto_f6(smp->mackey, smp->prnd, smp->rrnd, r, &smp->preq[1],
|
|
&smp->chan.chan.conn->le.init_addr, &smp->chan.chan.conn->le.resp_addr,
|
|
e)) {
|
|
LOG_ERR("Calculate local DHKey check failed");
|
|
return BT_SMP_ERR_UNSPECIFIED;
|
|
}
|
|
|
|
atomic_set_bit(smp->allowed_cmds, BT_SMP_DHKEY_CHECK);
|
|
return sc_smp_send_dhkey_check(smp, e);
|
|
}
|
|
#endif /* CONFIG_BT_CENTRAL */
|
|
|
|
#if defined(CONFIG_BT_PERIPHERAL)
|
|
static uint8_t compute_and_check_and_send_periph_dhcheck(struct bt_smp *smp)
|
|
{
|
|
uint8_t re[16], e[16], r[16];
|
|
uint8_t err;
|
|
|
|
(void)memset(r, 0, sizeof(r));
|
|
|
|
switch (smp->method) {
|
|
case JUST_WORKS:
|
|
case PASSKEY_CONFIRM:
|
|
break;
|
|
case PASSKEY_DISPLAY:
|
|
case PASSKEY_INPUT:
|
|
memcpy(r, &smp->passkey, sizeof(smp->passkey));
|
|
break;
|
|
case LE_SC_OOB:
|
|
if (smp->oobd_remote) {
|
|
memcpy(r, smp->oobd_remote->r, sizeof(r));
|
|
}
|
|
break;
|
|
default:
|
|
LOG_ERR("Unknown pairing method (%u)", smp->method);
|
|
return BT_SMP_ERR_UNSPECIFIED;
|
|
}
|
|
|
|
/* calculate LTK and mackey */
|
|
if (bt_crypto_f5(smp->dhkey, smp->rrnd, smp->prnd, &smp->chan.chan.conn->le.init_addr,
|
|
&smp->chan.chan.conn->le.resp_addr, smp->mackey, smp->tk)) {
|
|
LOG_ERR("Calculate LTK failed");
|
|
return BT_SMP_ERR_UNSPECIFIED;
|
|
}
|
|
|
|
/* calculate local DHKey check */
|
|
if (bt_crypto_f6(smp->mackey, smp->prnd, smp->rrnd, r, &smp->prsp[1],
|
|
&smp->chan.chan.conn->le.resp_addr, &smp->chan.chan.conn->le.init_addr,
|
|
e)) {
|
|
LOG_ERR("Calculate local DHKey check failed");
|
|
return BT_SMP_ERR_UNSPECIFIED;
|
|
}
|
|
|
|
if (smp->method == LE_SC_OOB) {
|
|
if (smp->oobd_local) {
|
|
memcpy(r, smp->oobd_local->r, sizeof(r));
|
|
} else {
|
|
memset(r, 0, sizeof(r));
|
|
}
|
|
}
|
|
|
|
/* calculate remote DHKey check */
|
|
if (bt_crypto_f6(smp->mackey, smp->rrnd, smp->prnd, r, &smp->preq[1],
|
|
&smp->chan.chan.conn->le.init_addr, &smp->chan.chan.conn->le.resp_addr,
|
|
re)) {
|
|
LOG_ERR("Calculate remote DHKey check failed");
|
|
return BT_SMP_ERR_UNSPECIFIED;
|
|
}
|
|
|
|
/* compare received E with calculated remote */
|
|
if (memcmp(smp->e, re, 16)) {
|
|
return BT_SMP_ERR_DHKEY_CHECK_FAILED;
|
|
}
|
|
|
|
/* send local e */
|
|
err = sc_smp_send_dhkey_check(smp, e);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
atomic_set_bit(smp->flags, SMP_FLAG_ENC_PENDING);
|
|
return 0;
|
|
}
|
|
#endif /* CONFIG_BT_PERIPHERAL */
|
|
|
|
static void bt_smp_dhkey_ready(const uint8_t *dhkey);
|
|
static uint8_t smp_dhkey_generate(struct bt_smp *smp)
|
|
{
|
|
int err;
|
|
|
|
atomic_set_bit(smp->flags, SMP_FLAG_DHKEY_GEN);
|
|
err = bt_dh_key_gen(smp->pkey, bt_smp_dhkey_ready);
|
|
if (err) {
|
|
atomic_clear_bit(smp->flags, SMP_FLAG_DHKEY_GEN);
|
|
|
|
LOG_ERR("Failed to generate DHKey");
|
|
return BT_SMP_ERR_UNSPECIFIED;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static uint8_t smp_dhkey_ready(struct bt_smp *smp, const uint8_t *dhkey)
|
|
{
|
|
if (!dhkey) {
|
|
return BT_SMP_ERR_DHKEY_CHECK_FAILED;
|
|
}
|
|
|
|
atomic_clear_bit(smp->flags, SMP_FLAG_DHKEY_PENDING);
|
|
memcpy(smp->dhkey, dhkey, BT_DH_KEY_LEN);
|
|
|
|
/* wait for user passkey confirmation */
|
|
if (atomic_test_bit(smp->flags, SMP_FLAG_USER)) {
|
|
atomic_set_bit(smp->flags, SMP_FLAG_DHKEY_SEND);
|
|
return 0;
|
|
}
|
|
|
|
/* wait for remote DHKey Check */
|
|
if (atomic_test_bit(smp->flags, SMP_FLAG_DHCHECK_WAIT)) {
|
|
atomic_set_bit(smp->flags, SMP_FLAG_DHKEY_SEND);
|
|
return 0;
|
|
}
|
|
|
|
if (atomic_test_bit(smp->flags, SMP_FLAG_DHKEY_SEND)) {
|
|
#if defined(CONFIG_BT_CENTRAL)
|
|
if (smp->chan.chan.conn->role == BT_HCI_ROLE_CENTRAL) {
|
|
return compute_and_send_central_dhcheck(smp);
|
|
}
|
|
|
|
#endif /* CONFIG_BT_CENTRAL */
|
|
|
|
#if defined(CONFIG_BT_PERIPHERAL)
|
|
return compute_and_check_and_send_periph_dhcheck(smp);
|
|
#endif /* CONFIG_BT_PERIPHERAL */
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct bt_smp *smp_find(int flag)
|
|
{
|
|
for (int i = 0; i < ARRAY_SIZE(bt_smp_pool); i++) {
|
|
if (atomic_test_bit(bt_smp_pool[i].flags, flag)) {
|
|
return &bt_smp_pool[i];
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void bt_smp_dhkey_ready(const uint8_t *dhkey)
|
|
{
|
|
LOG_DBG("%p", (void *)dhkey);
|
|
int err;
|
|
|
|
struct bt_smp *smp = smp_find(SMP_FLAG_DHKEY_GEN);
|
|
if (smp) {
|
|
atomic_clear_bit(smp->flags, SMP_FLAG_DHKEY_GEN);
|
|
err = smp_dhkey_ready(smp, dhkey);
|
|
if (err) {
|
|
smp_error(smp, err);
|
|
}
|
|
}
|
|
|
|
err = 0;
|
|
do {
|
|
smp = smp_find(SMP_FLAG_DHKEY_PENDING);
|
|
if (smp) {
|
|
err = smp_dhkey_generate(smp);
|
|
if (err) {
|
|
smp_error(smp, err);
|
|
}
|
|
}
|
|
} while (smp && err);
|
|
}
|
|
|
|
static uint8_t sc_smp_check_confirm(struct bt_smp *smp)
|
|
{
|
|
uint8_t cfm[16];
|
|
uint8_t r;
|
|
|
|
switch (smp->method) {
|
|
case LE_SC_OOB:
|
|
return 0;
|
|
case PASSKEY_CONFIRM:
|
|
case JUST_WORKS:
|
|
r = 0U;
|
|
break;
|
|
case PASSKEY_DISPLAY:
|
|
case PASSKEY_INPUT:
|
|
/*
|
|
* In the Passkey Entry protocol, the most significant
|
|
* bit of Z is set equal to one and the least
|
|
* significant bit is made up from one bit of the
|
|
* passkey e.g. if the passkey bit is 1, then Z = 0x81
|
|
* and if the passkey bit is 0, then Z = 0x80.
|
|
*/
|
|
r = (smp->passkey >> smp->passkey_round) & 0x01;
|
|
r |= 0x80;
|
|
break;
|
|
default:
|
|
LOG_ERR("Unknown pairing method (%u)", smp->method);
|
|
return BT_SMP_ERR_UNSPECIFIED;
|
|
}
|
|
|
|
if (bt_crypto_f4(smp->pkey, sc_public_key, smp->rrnd, r, cfm)) {
|
|
LOG_ERR("Calculate confirm failed");
|
|
return BT_SMP_ERR_UNSPECIFIED;
|
|
}
|
|
|
|
LOG_DBG("pcnf %s", bt_hex(smp->pcnf, 16));
|
|
LOG_DBG("cfm %s", bt_hex(cfm, 16));
|
|
|
|
if (memcmp(smp->pcnf, cfm, 16)) {
|
|
return BT_SMP_ERR_CONFIRM_FAILED;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static bool le_sc_oob_data_req_check(struct bt_smp *smp)
|
|
{
|
|
struct bt_smp_pairing *req = (struct bt_smp_pairing *)&smp->preq[1];
|
|
|
|
return ((req->oob_flag & BT_SMP_OOB_DATA_MASK) == BT_SMP_OOB_PRESENT);
|
|
}
|
|
|
|
static bool le_sc_oob_data_rsp_check(struct bt_smp *smp)
|
|
{
|
|
struct bt_smp_pairing *rsp = (struct bt_smp_pairing *)&smp->prsp[1];
|
|
|
|
return ((rsp->oob_flag & BT_SMP_OOB_DATA_MASK) == BT_SMP_OOB_PRESENT);
|
|
}
|
|
|
|
static void le_sc_oob_config_set(struct bt_smp *smp,
|
|
struct bt_conn_oob_info *info)
|
|
{
|
|
bool req_oob_present = le_sc_oob_data_req_check(smp);
|
|
bool rsp_oob_present = le_sc_oob_data_rsp_check(smp);
|
|
int oob_config = BT_CONN_OOB_NO_DATA;
|
|
|
|
if (IS_ENABLED(CONFIG_BT_CENTRAL) &&
|
|
smp->chan.chan.conn->role == BT_HCI_ROLE_CENTRAL) {
|
|
oob_config = req_oob_present ? BT_CONN_OOB_REMOTE_ONLY :
|
|
BT_CONN_OOB_NO_DATA;
|
|
|
|
if (rsp_oob_present) {
|
|
oob_config = (oob_config == BT_CONN_OOB_REMOTE_ONLY) ?
|
|
BT_CONN_OOB_BOTH_PEERS :
|
|
BT_CONN_OOB_LOCAL_ONLY;
|
|
}
|
|
} else if (IS_ENABLED(CONFIG_BT_PERIPHERAL)) {
|
|
oob_config = req_oob_present ? BT_CONN_OOB_LOCAL_ONLY :
|
|
BT_CONN_OOB_NO_DATA;
|
|
|
|
if (rsp_oob_present) {
|
|
oob_config = (oob_config == BT_CONN_OOB_LOCAL_ONLY) ?
|
|
BT_CONN_OOB_BOTH_PEERS :
|
|
BT_CONN_OOB_REMOTE_ONLY;
|
|
}
|
|
}
|
|
|
|
info->lesc.oob_config = oob_config;
|
|
}
|
|
|
|
static uint8_t smp_pairing_random(struct bt_smp *smp, struct net_buf *buf)
|
|
{
|
|
const struct bt_conn_auth_cb *smp_auth_cb = latch_auth_cb(smp);
|
|
struct bt_smp_pairing_random *req = (void *)buf->data;
|
|
uint32_t passkey;
|
|
uint8_t err;
|
|
|
|
LOG_DBG("");
|
|
|
|
memcpy(smp->rrnd, req->val, sizeof(smp->rrnd));
|
|
|
|
#if !defined(CONFIG_BT_SMP_SC_PAIR_ONLY)
|
|
if (!atomic_test_bit(smp->flags, SMP_FLAG_SC)) {
|
|
return legacy_pairing_random(smp);
|
|
}
|
|
#endif /* !CONFIG_BT_SMP_SC_PAIR_ONLY */
|
|
|
|
#if defined(CONFIG_BT_CENTRAL)
|
|
if (smp->chan.chan.conn->role == BT_HCI_ROLE_CENTRAL) {
|
|
err = sc_smp_check_confirm(smp);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
switch (smp->method) {
|
|
case PASSKEY_CONFIRM:
|
|
/* compare passkey before calculating LTK */
|
|
if (bt_crypto_g2(sc_public_key, smp->pkey, smp->prnd, smp->rrnd,
|
|
&passkey)) {
|
|
return BT_SMP_ERR_UNSPECIFIED;
|
|
}
|
|
|
|
atomic_set_bit(smp->flags, SMP_FLAG_USER);
|
|
atomic_set_bit(smp->flags, SMP_FLAG_DHKEY_SEND);
|
|
smp_auth_cb->passkey_confirm(smp->chan.chan.conn, passkey);
|
|
return 0;
|
|
case JUST_WORKS:
|
|
break;
|
|
case LE_SC_OOB:
|
|
break;
|
|
case PASSKEY_DISPLAY:
|
|
case PASSKEY_INPUT:
|
|
smp->passkey_round++;
|
|
if (smp->passkey_round == 20U) {
|
|
break;
|
|
}
|
|
|
|
if (bt_rand(smp->prnd, 16)) {
|
|
return BT_SMP_ERR_UNSPECIFIED;
|
|
}
|
|
|
|
atomic_set_bit(smp->allowed_cmds,
|
|
BT_SMP_CMD_PAIRING_CONFIRM);
|
|
return smp_send_pairing_confirm(smp);
|
|
default:
|
|
LOG_ERR("Unknown pairing method (%u)", smp->method);
|
|
return BT_SMP_ERR_UNSPECIFIED;
|
|
}
|
|
|
|
/* wait for DHKey being generated */
|
|
if (atomic_test_bit(smp->flags, SMP_FLAG_DHKEY_PENDING)) {
|
|
atomic_set_bit(smp->flags, SMP_FLAG_DHKEY_SEND);
|
|
return 0;
|
|
}
|
|
|
|
return compute_and_send_central_dhcheck(smp);
|
|
}
|
|
#endif /* CONFIG_BT_CENTRAL */
|
|
|
|
#if defined(CONFIG_BT_PERIPHERAL)
|
|
switch (smp->method) {
|
|
case PASSKEY_CONFIRM:
|
|
if (bt_crypto_g2(smp->pkey, sc_public_key, smp->rrnd, smp->prnd, &passkey)) {
|
|
return BT_SMP_ERR_UNSPECIFIED;
|
|
}
|
|
|
|
atomic_set_bit(smp->flags, SMP_FLAG_USER);
|
|
smp_auth_cb->passkey_confirm(smp->chan.chan.conn, passkey);
|
|
break;
|
|
case JUST_WORKS:
|
|
break;
|
|
case PASSKEY_DISPLAY:
|
|
case PASSKEY_INPUT:
|
|
err = sc_smp_check_confirm(smp);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
atomic_set_bit(smp->allowed_cmds,
|
|
BT_SMP_CMD_PAIRING_CONFIRM);
|
|
err = smp_send_pairing_random(smp);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
smp->passkey_round++;
|
|
if (smp->passkey_round == 20U) {
|
|
atomic_set_bit(smp->allowed_cmds, BT_SMP_DHKEY_CHECK);
|
|
atomic_set_bit(smp->flags, SMP_FLAG_DHCHECK_WAIT);
|
|
return 0;
|
|
}
|
|
|
|
if (bt_rand(smp->prnd, 16)) {
|
|
return BT_SMP_ERR_UNSPECIFIED;
|
|
}
|
|
|
|
return 0;
|
|
case LE_SC_OOB:
|
|
/* Step 6: Select random N */
|
|
if (bt_rand(smp->prnd, 16)) {
|
|
return BT_SMP_ERR_UNSPECIFIED;
|
|
}
|
|
|
|
if (smp_auth_cb && smp_auth_cb->oob_data_request) {
|
|
struct bt_conn_oob_info info = {
|
|
.type = BT_CONN_OOB_LE_SC,
|
|
.lesc.oob_config = BT_CONN_OOB_NO_DATA,
|
|
};
|
|
|
|
le_sc_oob_config_set(smp, &info);
|
|
|
|
smp->oobd_local = NULL;
|
|
smp->oobd_remote = NULL;
|
|
|
|
atomic_set_bit(smp->flags, SMP_FLAG_OOB_PENDING);
|
|
smp_auth_cb->oob_data_request(smp->chan.chan.conn, &info);
|
|
|
|
return 0;
|
|
} else {
|
|
return BT_SMP_ERR_OOB_NOT_AVAIL;
|
|
}
|
|
default:
|
|
LOG_ERR("Unknown pairing method (%u)", smp->method);
|
|
return BT_SMP_ERR_UNSPECIFIED;
|
|
}
|
|
|
|
atomic_set_bit(smp->allowed_cmds, BT_SMP_DHKEY_CHECK);
|
|
atomic_set_bit(smp->flags, SMP_FLAG_DHCHECK_WAIT);
|
|
return smp_send_pairing_random(smp);
|
|
#else
|
|
return BT_SMP_ERR_PAIRING_NOTSUPP;
|
|
#endif /* CONFIG_BT_PERIPHERAL */
|
|
}
|
|
|
|
static uint8_t smp_pairing_failed(struct bt_smp *smp, struct net_buf *buf)
|
|
{
|
|
struct bt_conn *conn = smp->chan.chan.conn;
|
|
const struct bt_conn_auth_cb *smp_auth_cb = latch_auth_cb(smp);
|
|
struct bt_smp_pairing_fail *req = (void *)buf->data;
|
|
|
|
LOG_ERR("pairing failed (peer reason 0x%x)", req->reason);
|
|
|
|
if (atomic_test_and_clear_bit(smp->flags, SMP_FLAG_USER) ||
|
|
atomic_test_and_clear_bit(smp->flags, SMP_FLAG_DISPLAY)) {
|
|
if (smp_auth_cb && smp_auth_cb->cancel) {
|
|
smp_auth_cb->cancel(conn);
|
|
}
|
|
}
|
|
|
|
smp_pairing_complete(smp, req->reason);
|
|
|
|
/* return no error to avoid sending Pairing Failed in response */
|
|
return 0;
|
|
}
|
|
|
|
static uint8_t smp_ident_info(struct bt_smp *smp, struct net_buf *buf)
|
|
{
|
|
LOG_DBG("");
|
|
|
|
if (atomic_test_bit(smp->flags, SMP_FLAG_BOND)) {
|
|
struct bt_smp_ident_info *req = (void *)buf->data;
|
|
struct bt_conn *conn = smp->chan.chan.conn;
|
|
struct bt_keys *keys;
|
|
|
|
keys = bt_keys_get_type(BT_KEYS_IRK, conn->id, &conn->le.dst);
|
|
if (!keys) {
|
|
LOG_ERR("Unable to get keys for %s", bt_addr_le_str(&conn->le.dst));
|
|
return BT_SMP_ERR_UNSPECIFIED;
|
|
}
|
|
|
|
memcpy(keys->irk.val, req->irk, 16);
|
|
}
|
|
|
|
atomic_set_bit(smp->allowed_cmds, BT_SMP_CMD_IDENT_ADDR_INFO);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static uint8_t smp_id_add_replace(struct bt_smp *smp, struct bt_keys *new_bond)
|
|
{
|
|
struct bt_keys *conflict;
|
|
|
|
/* Sanity check: It does not make sense to finalize a bond before we
|
|
* have the remote identity.
|
|
*/
|
|
__ASSERT_NO_MSG(!(smp->remote_dist & BT_SMP_DIST_ID_KEY));
|
|
|
|
conflict = bt_id_find_conflict(new_bond);
|
|
if (conflict) {
|
|
LOG_DBG("New bond conflicts with a bond on id %d.", conflict->id);
|
|
}
|
|
|
|
if (conflict && !IS_ENABLED(CONFIG_BT_ID_UNPAIR_MATCHING_BONDS)) {
|
|
LOG_WRN("Refusing new pairing. The old bond must be unpaired first.");
|
|
return BT_SMP_ERR_AUTH_REQUIREMENTS;
|
|
}
|
|
|
|
if (conflict && IS_ENABLED(CONFIG_BT_ID_UNPAIR_MATCHING_BONDS)) {
|
|
bool trust_ok;
|
|
int unpair_err;
|
|
|
|
trust_ok = update_keys_check(smp, conflict);
|
|
if (!trust_ok) {
|
|
LOG_WRN("Refusing new pairing. The old bond has more trust.");
|
|
return BT_SMP_ERR_AUTH_REQUIREMENTS;
|
|
}
|
|
|
|
LOG_DBG("Un-pairing old conflicting bond and finalizing new.");
|
|
|
|
unpair_err = bt_unpair(conflict->id, &conflict->addr);
|
|
__ASSERT_NO_MSG(!unpair_err);
|
|
}
|
|
|
|
__ASSERT_NO_MSG(!bt_id_find_conflict(new_bond));
|
|
bt_id_add(new_bond);
|
|
return 0;
|
|
}
|
|
|
|
struct addr_match {
|
|
const bt_addr_le_t *rpa;
|
|
const bt_addr_le_t *id_addr;
|
|
};
|
|
|
|
static void convert_to_id_on_match(struct bt_conn *conn, void *data)
|
|
{
|
|
struct addr_match *addr_match = data;
|
|
|
|
if (bt_addr_le_eq(&conn->le.dst, addr_match->rpa)) {
|
|
bt_addr_le_copy(&conn->le.dst, addr_match->id_addr);
|
|
}
|
|
}
|
|
|
|
static uint8_t smp_ident_addr_info(struct bt_smp *smp, struct net_buf *buf)
|
|
{
|
|
struct bt_conn *conn = smp->chan.chan.conn;
|
|
struct bt_smp_ident_addr_info *req = (void *)buf->data;
|
|
uint8_t err;
|
|
|
|
LOG_DBG("identity %s", bt_addr_le_str(&req->addr));
|
|
|
|
smp->remote_dist &= ~BT_SMP_DIST_ID_KEY;
|
|
|
|
if (!bt_addr_le_is_identity(&req->addr)) {
|
|
LOG_ERR("Invalid identity %s", bt_addr_le_str(&req->addr));
|
|
LOG_ERR(" for %s", bt_addr_le_str(&conn->le.dst));
|
|
return BT_SMP_ERR_INVALID_PARAMS;
|
|
}
|
|
|
|
if (!bt_addr_le_eq(&conn->le.dst, &req->addr)) {
|
|
struct bt_keys *keys = bt_keys_find_addr(conn->id, &req->addr);
|
|
|
|
if (keys) {
|
|
if (!update_keys_check(smp, keys)) {
|
|
return BT_SMP_ERR_UNSPECIFIED;
|
|
}
|
|
|
|
bt_keys_clear(keys);
|
|
}
|
|
}
|
|
|
|
if (atomic_test_bit(smp->flags, SMP_FLAG_BOND)) {
|
|
const bt_addr_le_t *dst;
|
|
struct bt_keys *keys;
|
|
|
|
keys = bt_keys_get_type(BT_KEYS_IRK, conn->id, &conn->le.dst);
|
|
if (!keys) {
|
|
LOG_ERR("Unable to get keys for %s", bt_addr_le_str(&conn->le.dst));
|
|
return BT_SMP_ERR_UNSPECIFIED;
|
|
}
|
|
|
|
/*
|
|
* We can't use conn->dst here as this might already contain
|
|
* identity address known from previous pairing. Since all keys
|
|
* are cleared on re-pairing we wouldn't store IRK distributed
|
|
* in new pairing.
|
|
*/
|
|
if (conn->role == BT_HCI_ROLE_CENTRAL) {
|
|
dst = &conn->le.resp_addr;
|
|
} else {
|
|
dst = &conn->le.init_addr;
|
|
}
|
|
|
|
if (bt_addr_le_is_rpa(dst)) {
|
|
/* always update last use RPA */
|
|
bt_addr_copy(&keys->irk.rpa, &dst->a);
|
|
|
|
/*
|
|
* Update connection address and notify about identity
|
|
* resolved only if connection wasn't already reported
|
|
* with identity address. This may happen if IRK was
|
|
* present before ie. due to re-pairing.
|
|
*/
|
|
if (!bt_addr_le_is_identity(&conn->le.dst)) {
|
|
struct addr_match addr_match = {
|
|
.rpa = &conn->le.dst,
|
|
.id_addr = &req->addr,
|
|
};
|
|
|
|
bt_conn_foreach(BT_CONN_TYPE_LE,
|
|
convert_to_id_on_match,
|
|
&addr_match);
|
|
bt_addr_le_copy(&keys->addr, &req->addr);
|
|
|
|
bt_conn_identity_resolved(conn);
|
|
}
|
|
}
|
|
|
|
err = smp_id_add_replace(smp, keys);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
}
|
|
|
|
if (smp->remote_dist & BT_SMP_DIST_SIGN) {
|
|
atomic_set_bit(smp->allowed_cmds, BT_SMP_CMD_SIGNING_INFO);
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_CENTRAL) &&
|
|
conn->role == BT_HCI_ROLE_CENTRAL && !smp->remote_dist) {
|
|
err = bt_smp_distribute_keys(smp);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
}
|
|
|
|
/* if all keys were distributed, pairing is done */
|
|
if (!smp->local_dist && !smp->remote_dist) {
|
|
smp_pairing_complete(smp, 0);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if defined(CONFIG_BT_SIGNING)
|
|
static uint8_t smp_signing_info(struct bt_smp *smp, struct net_buf *buf)
|
|
{
|
|
struct bt_conn *conn = smp->chan.chan.conn;
|
|
uint8_t err;
|
|
|
|
LOG_DBG("");
|
|
|
|
if (atomic_test_bit(smp->flags, SMP_FLAG_BOND)) {
|
|
struct bt_smp_signing_info *req = (void *)buf->data;
|
|
struct bt_keys *keys;
|
|
|
|
keys = bt_keys_get_type(BT_KEYS_REMOTE_CSRK, conn->id,
|
|
&conn->le.dst);
|
|
if (!keys) {
|
|
LOG_ERR("Unable to get keys for %s", bt_addr_le_str(&conn->le.dst));
|
|
return BT_SMP_ERR_UNSPECIFIED;
|
|
}
|
|
|
|
memcpy(keys->remote_csrk.val, req->csrk,
|
|
sizeof(keys->remote_csrk.val));
|
|
}
|
|
|
|
smp->remote_dist &= ~BT_SMP_DIST_SIGN;
|
|
|
|
if (IS_ENABLED(CONFIG_BT_CENTRAL) &&
|
|
conn->role == BT_HCI_ROLE_CENTRAL && !smp->remote_dist) {
|
|
err = bt_smp_distribute_keys(smp);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
}
|
|
|
|
/* if all keys were distributed, pairing is done */
|
|
if (!smp->local_dist && !smp->remote_dist) {
|
|
smp_pairing_complete(smp, 0);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#else
|
|
static uint8_t smp_signing_info(struct bt_smp *smp, struct net_buf *buf)
|
|
{
|
|
return BT_SMP_ERR_CMD_NOTSUPP;
|
|
}
|
|
#endif /* CONFIG_BT_SIGNING */
|
|
|
|
#if defined(CONFIG_BT_CENTRAL)
|
|
static uint8_t smp_security_request(struct bt_smp *smp, struct net_buf *buf)
|
|
{
|
|
struct bt_conn *conn = smp->chan.chan.conn;
|
|
struct bt_smp_security_request *req = (void *)buf->data;
|
|
uint8_t auth;
|
|
|
|
LOG_DBG("");
|
|
|
|
/* A higher security level is requested during the key distribution
|
|
* phase, once pairing is complete a new pairing procedure will start.
|
|
*/
|
|
if (atomic_test_bit(smp->flags, SMP_FLAG_KEYS_DISTR)) {
|
|
return 0;
|
|
}
|
|
|
|
if (atomic_test_bit(smp->flags, SMP_FLAG_PAIRING)) {
|
|
/* We have already started pairing process */
|
|
return 0;
|
|
}
|
|
|
|
if (atomic_test_bit(smp->flags, SMP_FLAG_ENC_PENDING)) {
|
|
/* We have already started encryption procedure */
|
|
return 0;
|
|
}
|
|
|
|
if (sc_supported) {
|
|
auth = req->auth_req & BT_SMP_AUTH_MASK_SC;
|
|
} else {
|
|
auth = req->auth_req & BT_SMP_AUTH_MASK;
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_SMP_SC_PAIR_ONLY) &&
|
|
!(auth & BT_SMP_AUTH_SC)) {
|
|
return BT_SMP_ERR_AUTH_REQUIREMENTS;
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_BONDING_REQUIRED) &&
|
|
!(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;
|
|
}
|
|
|
|
if (conn->le.keys) {
|
|
/* Make sure we have an LTK to encrypt with */
|
|
if (!(conn->le.keys->keys & (BT_KEYS_LTK_P256 | BT_KEYS_LTK))) {
|
|
goto pair;
|
|
}
|
|
} else {
|
|
conn->le.keys = bt_keys_find(BT_KEYS_LTK_P256, conn->id,
|
|
&conn->le.dst);
|
|
if (!conn->le.keys) {
|
|
conn->le.keys = bt_keys_find(BT_KEYS_LTK, conn->id,
|
|
&conn->le.dst);
|
|
}
|
|
}
|
|
|
|
if (!conn->le.keys) {
|
|
goto pair;
|
|
}
|
|
|
|
/* if MITM required key must be authenticated */
|
|
if ((auth & BT_SMP_AUTH_MITM) &&
|
|
!(conn->le.keys->flags & BT_KEYS_AUTHENTICATED)) {
|
|
if (get_io_capa(smp) != BT_SMP_IO_NO_INPUT_OUTPUT) {
|
|
LOG_INF("New auth requirements: 0x%x, repairing", auth);
|
|
goto pair;
|
|
}
|
|
|
|
LOG_WRN("Unsupported auth requirements: 0x%x, repairing", auth);
|
|
goto pair;
|
|
}
|
|
|
|
/* if LE SC required and no p256 key present repair */
|
|
if ((auth & BT_SMP_AUTH_SC) &&
|
|
!(conn->le.keys->keys & BT_KEYS_LTK_P256)) {
|
|
LOG_INF("New auth requirements: 0x%x, repairing", auth);
|
|
goto pair;
|
|
}
|
|
|
|
if (bt_conn_le_start_encryption(conn, conn->le.keys->ltk.rand,
|
|
conn->le.keys->ltk.ediv,
|
|
conn->le.keys->ltk.val,
|
|
conn->le.keys->enc_size) < 0) {
|
|
LOG_ERR("Failed to start encryption");
|
|
return BT_SMP_ERR_UNSPECIFIED;
|
|
}
|
|
|
|
atomic_set_bit(smp->flags, SMP_FLAG_ENC_PENDING);
|
|
|
|
return 0;
|
|
pair:
|
|
if (smp_send_pairing_req(conn) < 0) {
|
|
return BT_SMP_ERR_UNSPECIFIED;
|
|
}
|
|
|
|
atomic_set_bit(smp->flags, SMP_FLAG_SEC_REQ);
|
|
|
|
return 0;
|
|
}
|
|
#else
|
|
static uint8_t smp_security_request(struct bt_smp *smp, struct net_buf *buf)
|
|
{
|
|
return BT_SMP_ERR_CMD_NOTSUPP;
|
|
}
|
|
#endif /* CONFIG_BT_CENTRAL */
|
|
|
|
static uint8_t generate_dhkey(struct bt_smp *smp)
|
|
{
|
|
if (IS_ENABLED(CONFIG_BT_SMP_OOB_LEGACY_PAIR_ONLY)) {
|
|
return BT_SMP_ERR_UNSPECIFIED;
|
|
}
|
|
|
|
atomic_set_bit(smp->flags, SMP_FLAG_DHKEY_PENDING);
|
|
if (!smp_find(SMP_FLAG_DHKEY_GEN)) {
|
|
return smp_dhkey_generate(smp);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
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);
|
|
|
|
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_round = 0U;
|
|
|
|
if (smp_auth_cb && smp_auth_cb->passkey_display) {
|
|
atomic_set_bit(smp->flags, SMP_FLAG_DISPLAY);
|
|
smp_auth_cb->passkey_display(conn, smp->passkey);
|
|
}
|
|
|
|
smp->passkey = sys_cpu_to_le32(smp->passkey);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if defined(CONFIG_BT_PERIPHERAL)
|
|
static uint8_t smp_public_key_periph(struct bt_smp *smp)
|
|
{
|
|
const struct bt_conn_auth_cb *smp_auth_cb = latch_auth_cb(smp);
|
|
uint8_t err;
|
|
|
|
if (!atomic_test_bit(smp->flags, SMP_FLAG_SC_DEBUG_KEY) &&
|
|
memcmp(smp->pkey, sc_public_key, BT_PUB_KEY_COORD_LEN) == 0) {
|
|
/* Deny public key with identitcal X coordinate unless it is the
|
|
* debug public key.
|
|
*/
|
|
LOG_WRN("Remote public key rejected");
|
|
return BT_SMP_ERR_UNSPECIFIED;
|
|
}
|
|
|
|
err = sc_send_public_key(smp);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
switch (smp->method) {
|
|
case PASSKEY_CONFIRM:
|
|
case JUST_WORKS:
|
|
atomic_set_bit(smp->allowed_cmds, BT_SMP_CMD_PAIRING_RANDOM);
|
|
|
|
err = smp_send_pairing_confirm(smp);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
break;
|
|
case PASSKEY_DISPLAY:
|
|
err = display_passkey(smp);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
atomic_set_bit(smp->allowed_cmds, BT_SMP_CMD_PAIRING_CONFIRM);
|
|
atomic_set_bit(smp->allowed_cmds, BT_SMP_KEYPRESS_NOTIFICATION);
|
|
break;
|
|
case PASSKEY_INPUT:
|
|
atomic_set_bit(smp->allowed_cmds, BT_SMP_CMD_PAIRING_CONFIRM);
|
|
atomic_set_bit(smp->allowed_cmds, BT_SMP_KEYPRESS_NOTIFICATION);
|
|
atomic_set_bit(smp->flags, SMP_FLAG_USER);
|
|
smp_auth_cb->passkey_entry(smp->chan.chan.conn);
|
|
break;
|
|
case LE_SC_OOB:
|
|
atomic_set_bit(smp->allowed_cmds, BT_SMP_CMD_PAIRING_RANDOM);
|
|
break;
|
|
default:
|
|
LOG_ERR("Unknown pairing method (%u)", smp->method);
|
|
return BT_SMP_ERR_UNSPECIFIED;
|
|
}
|
|
|
|
return generate_dhkey(smp);
|
|
}
|
|
#endif /* CONFIG_BT_PERIPHERAL */
|
|
|
|
static uint8_t smp_public_key(struct bt_smp *smp, struct net_buf *buf)
|
|
{
|
|
const struct bt_conn_auth_cb *smp_auth_cb = latch_auth_cb(smp);
|
|
struct bt_smp_public_key *req = (void *)buf->data;
|
|
uint8_t err;
|
|
|
|
LOG_DBG("");
|
|
|
|
memcpy(smp->pkey, req->x, BT_PUB_KEY_COORD_LEN);
|
|
memcpy(&smp->pkey[BT_PUB_KEY_COORD_LEN], req->y, BT_PUB_KEY_COORD_LEN);
|
|
|
|
/* mark key as debug if remote is using it */
|
|
if (bt_pub_key_is_debug(smp->pkey)) {
|
|
LOG_INF("Remote is using Debug Public key");
|
|
atomic_set_bit(smp->flags, SMP_FLAG_SC_DEBUG_KEY);
|
|
|
|
/* Don't allow a bond established without debug key to be
|
|
* updated using LTK generated from debug key.
|
|
*/
|
|
if (!update_debug_keys_check(smp)) {
|
|
return BT_SMP_ERR_AUTH_REQUIREMENTS;
|
|
}
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_CENTRAL) &&
|
|
smp->chan.chan.conn->role == BT_HCI_ROLE_CENTRAL) {
|
|
if (!atomic_test_bit(smp->flags, SMP_FLAG_SC_DEBUG_KEY) &&
|
|
memcmp(smp->pkey, sc_public_key, BT_PUB_KEY_COORD_LEN) == 0) {
|
|
/* Deny public key with identitcal X coordinate unless
|
|
* it is the debug public key.
|
|
*/
|
|
LOG_WRN("Remote public key rejected");
|
|
return BT_SMP_ERR_UNSPECIFIED;
|
|
}
|
|
|
|
switch (smp->method) {
|
|
case PASSKEY_CONFIRM:
|
|
case JUST_WORKS:
|
|
atomic_set_bit(smp->allowed_cmds,
|
|
BT_SMP_CMD_PAIRING_CONFIRM);
|
|
break;
|
|
case PASSKEY_DISPLAY:
|
|
err = display_passkey(smp);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
atomic_set_bit(smp->allowed_cmds,
|
|
BT_SMP_CMD_PAIRING_CONFIRM);
|
|
|
|
atomic_set_bit(smp->allowed_cmds,
|
|
BT_SMP_KEYPRESS_NOTIFICATION);
|
|
|
|
err = smp_send_pairing_confirm(smp);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
break;
|
|
case PASSKEY_INPUT:
|
|
atomic_set_bit(smp->flags, SMP_FLAG_USER);
|
|
smp_auth_cb->passkey_entry(smp->chan.chan.conn);
|
|
|
|
atomic_set_bit(smp->allowed_cmds,
|
|
BT_SMP_KEYPRESS_NOTIFICATION);
|
|
|
|
break;
|
|
case LE_SC_OOB:
|
|
/* Step 6: Select random N */
|
|
if (bt_rand(smp->prnd, 16)) {
|
|
return BT_SMP_ERR_UNSPECIFIED;
|
|
}
|
|
|
|
if (smp_auth_cb && smp_auth_cb->oob_data_request) {
|
|
struct bt_conn_oob_info info = {
|
|
.type = BT_CONN_OOB_LE_SC,
|
|
.lesc.oob_config = BT_CONN_OOB_NO_DATA,
|
|
};
|
|
|
|
le_sc_oob_config_set(smp, &info);
|
|
|
|
smp->oobd_local = NULL;
|
|
smp->oobd_remote = NULL;
|
|
|
|
atomic_set_bit(smp->flags,
|
|
SMP_FLAG_OOB_PENDING);
|
|
smp_auth_cb->oob_data_request(smp->chan.chan.conn, &info);
|
|
} else {
|
|
return BT_SMP_ERR_OOB_NOT_AVAIL;
|
|
}
|
|
break;
|
|
default:
|
|
LOG_ERR("Unknown pairing method (%u)", smp->method);
|
|
return BT_SMP_ERR_UNSPECIFIED;
|
|
}
|
|
|
|
return generate_dhkey(smp);
|
|
}
|
|
|
|
#if defined(CONFIG_BT_PERIPHERAL)
|
|
if (!sc_public_key) {
|
|
atomic_set_bit(smp->flags, SMP_FLAG_PKEY_SEND);
|
|
return 0;
|
|
}
|
|
|
|
err = smp_public_key_periph(smp);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
#endif /* CONFIG_BT_PERIPHERAL */
|
|
|
|
return 0;
|
|
}
|
|
|
|
static uint8_t smp_dhkey_check(struct bt_smp *smp, struct net_buf *buf)
|
|
{
|
|
struct bt_smp_dhkey_check *req = (void *)buf->data;
|
|
|
|
LOG_DBG("");
|
|
|
|
if (IS_ENABLED(CONFIG_BT_CENTRAL) &&
|
|
smp->chan.chan.conn->role == BT_HCI_ROLE_CENTRAL) {
|
|
uint8_t e[16], r[16], enc_size;
|
|
uint8_t ediv[2], rand[8];
|
|
|
|
(void)memset(r, 0, sizeof(r));
|
|
|
|
switch (smp->method) {
|
|
case JUST_WORKS:
|
|
case PASSKEY_CONFIRM:
|
|
break;
|
|
case PASSKEY_DISPLAY:
|
|
case PASSKEY_INPUT:
|
|
memcpy(r, &smp->passkey, sizeof(smp->passkey));
|
|
break;
|
|
case LE_SC_OOB:
|
|
if (smp->oobd_local) {
|
|
memcpy(r, smp->oobd_local->r, sizeof(r));
|
|
}
|
|
break;
|
|
default:
|
|
LOG_ERR("Unknown pairing method (%u)", smp->method);
|
|
return BT_SMP_ERR_UNSPECIFIED;
|
|
}
|
|
|
|
/* calculate remote DHKey check for comparison */
|
|
if (bt_crypto_f6(smp->mackey, smp->rrnd, smp->prnd, r, &smp->prsp[1],
|
|
&smp->chan.chan.conn->le.resp_addr,
|
|
&smp->chan.chan.conn->le.init_addr, e)) {
|
|
return BT_SMP_ERR_UNSPECIFIED;
|
|
}
|
|
|
|
if (memcmp(e, req->e, 16)) {
|
|
return BT_SMP_ERR_DHKEY_CHECK_FAILED;
|
|
}
|
|
|
|
enc_size = get_encryption_key_size(smp);
|
|
|
|
/* Rand and EDiv are 0 */
|
|
(void)memset(ediv, 0, sizeof(ediv));
|
|
(void)memset(rand, 0, sizeof(rand));
|
|
if (bt_conn_le_start_encryption(smp->chan.chan.conn, rand, ediv,
|
|
smp->tk, enc_size) < 0) {
|
|
LOG_ERR("Failed to start encryption");
|
|
return BT_SMP_ERR_UNSPECIFIED;
|
|
}
|
|
|
|
atomic_set_bit(smp->flags, SMP_FLAG_ENC_PENDING);
|
|
|
|
if (IS_ENABLED(CONFIG_BT_SMP_USB_HCI_CTLR_WORKAROUND)) {
|
|
if (smp->remote_dist & BT_SMP_DIST_ID_KEY) {
|
|
atomic_set_bit(smp->allowed_cmds,
|
|
BT_SMP_CMD_IDENT_INFO);
|
|
} else if (smp->remote_dist & BT_SMP_DIST_SIGN) {
|
|
atomic_set_bit(smp->allowed_cmds,
|
|
BT_SMP_CMD_SIGNING_INFO);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if defined(CONFIG_BT_PERIPHERAL)
|
|
if (smp->chan.chan.conn->role == BT_HCI_ROLE_PERIPHERAL) {
|
|
atomic_clear_bit(smp->flags, SMP_FLAG_DHCHECK_WAIT);
|
|
memcpy(smp->e, req->e, sizeof(smp->e));
|
|
|
|
/* wait for DHKey being generated */
|
|
if (atomic_test_bit(smp->flags, SMP_FLAG_DHKEY_PENDING)) {
|
|
atomic_set_bit(smp->flags, SMP_FLAG_DHKEY_SEND);
|
|
return 0;
|
|
}
|
|
|
|
/* waiting for user to confirm passkey */
|
|
if (atomic_test_bit(smp->flags, SMP_FLAG_USER)) {
|
|
atomic_set_bit(smp->flags, SMP_FLAG_DHKEY_SEND);
|
|
return 0;
|
|
}
|
|
|
|
return compute_and_check_and_send_periph_dhcheck(smp);
|
|
}
|
|
#endif /* CONFIG_BT_PERIPHERAL */
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if defined(CONFIG_BT_PASSKEY_KEYPRESS)
|
|
static uint8_t smp_keypress_notif(struct bt_smp *smp, struct net_buf *buf)
|
|
{
|
|
const struct bt_conn_auth_cb *smp_auth_cb = latch_auth_cb(smp);
|
|
struct bt_conn *conn = smp->chan.chan.conn;
|
|
struct bt_smp_keypress_notif *notif = (void *)buf->data;
|
|
enum bt_conn_auth_keypress type = notif->type;
|
|
|
|
LOG_DBG("Keypress from conn %u, type %u", bt_conn_index(conn), type);
|
|
|
|
/* For now, keypress notifications are always accepted. In the future we
|
|
* should be smarter about this. We might also want to enforce something
|
|
* about the 'start' and 'end' messages.
|
|
*/
|
|
atomic_set_bit(smp->allowed_cmds, BT_SMP_KEYPRESS_NOTIFICATION);
|
|
|
|
if (!IN_RANGE(type,
|
|
BT_CONN_AUTH_KEYPRESS_ENTRY_STARTED,
|
|
BT_CONN_AUTH_KEYPRESS_ENTRY_COMPLETED)) {
|
|
LOG_WRN("Received unknown keypress event type %u. Discarding.", type);
|
|
return BT_SMP_ERR_INVALID_PARAMS;
|
|
}
|
|
|
|
/* Reset SMP timeout, like the spec says. */
|
|
k_work_reschedule(&smp->work, SMP_TIMEOUT);
|
|
|
|
if (smp_auth_cb->passkey_display_keypress) {
|
|
smp_auth_cb->passkey_display_keypress(conn, type);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#else
|
|
static uint8_t smp_keypress_notif(struct bt_smp *smp, struct net_buf *buf)
|
|
{
|
|
ARG_UNUSED(smp);
|
|
ARG_UNUSED(buf);
|
|
|
|
LOG_DBG("");
|
|
|
|
/* Ignore packets until keypress notifications are fully supported. */
|
|
atomic_set_bit(smp->allowed_cmds, BT_SMP_KEYPRESS_NOTIFICATION);
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static const struct {
|
|
uint8_t (*func)(struct bt_smp *smp, struct net_buf *buf);
|
|
uint8_t expect_len;
|
|
} handlers[] = {
|
|
{ }, /* No op-code defined for 0x00 */
|
|
{ smp_pairing_req, sizeof(struct bt_smp_pairing) },
|
|
{ smp_pairing_rsp, sizeof(struct bt_smp_pairing) },
|
|
{ smp_pairing_confirm, sizeof(struct bt_smp_pairing_confirm) },
|
|
{ smp_pairing_random, sizeof(struct bt_smp_pairing_random) },
|
|
{ smp_pairing_failed, sizeof(struct bt_smp_pairing_fail) },
|
|
{ smp_encrypt_info, sizeof(struct bt_smp_encrypt_info) },
|
|
{ smp_central_ident, sizeof(struct bt_smp_central_ident) },
|
|
{ smp_ident_info, sizeof(struct bt_smp_ident_info) },
|
|
{ smp_ident_addr_info, sizeof(struct bt_smp_ident_addr_info) },
|
|
{ smp_signing_info, sizeof(struct bt_smp_signing_info) },
|
|
{ smp_security_request, sizeof(struct bt_smp_security_request) },
|
|
{ smp_public_key, sizeof(struct bt_smp_public_key) },
|
|
{ smp_dhkey_check, sizeof(struct bt_smp_dhkey_check) },
|
|
{ smp_keypress_notif, sizeof(struct bt_smp_keypress_notif) },
|
|
};
|
|
|
|
static bool is_in_pairing_procedure(struct bt_smp *smp)
|
|
{
|
|
return atomic_test_bit(smp->flags, SMP_FLAG_PAIRING);
|
|
}
|
|
|
|
static int bt_smp_recv(struct bt_l2cap_chan *chan, struct net_buf *buf)
|
|
{
|
|
struct bt_smp *smp = CONTAINER_OF(chan, struct bt_smp, chan.chan);
|
|
struct bt_smp_hdr *hdr;
|
|
uint8_t err;
|
|
|
|
if (buf->len < sizeof(*hdr)) {
|
|
LOG_ERR("Too small SMP PDU received");
|
|
return 0;
|
|
}
|
|
|
|
hdr = net_buf_pull_mem(buf, sizeof(*hdr));
|
|
LOG_DBG("Received SMP code 0x%02x len %u", hdr->code, buf->len);
|
|
|
|
/*
|
|
* If SMP timeout occurred "no further SMP commands shall be sent over
|
|
* the L2CAP Security Manager Channel. A new SM procedure shall only be
|
|
* performed when a new physical link has been established."
|
|
*/
|
|
if (atomic_test_bit(smp->flags, SMP_FLAG_TIMEOUT)) {
|
|
LOG_WRN("SMP command (code 0x%02x) received after timeout", hdr->code);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Bluetooth Core Specification Version 5.2, Vol 3, Part H, page 1667:
|
|
* If a packet is received with a Code that is reserved for future use
|
|
* it shall be ignored.
|
|
*/
|
|
if (hdr->code >= ARRAY_SIZE(handlers)) {
|
|
LOG_WRN("Received reserved SMP code 0x%02x", hdr->code);
|
|
return 0;
|
|
}
|
|
|
|
if (!handlers[hdr->code].func) {
|
|
LOG_WRN("Unhandled SMP code 0x%02x", hdr->code);
|
|
smp_error(smp, BT_SMP_ERR_CMD_NOTSUPP);
|
|
return 0;
|
|
}
|
|
|
|
if (!atomic_test_and_clear_bit(smp->allowed_cmds, hdr->code)) {
|
|
LOG_WRN("Unexpected SMP code 0x%02x", hdr->code);
|
|
/* Do not send errors outside of pairing procedure. */
|
|
if (is_in_pairing_procedure(smp)) {
|
|
smp_error(smp, BT_SMP_ERR_UNSPECIFIED);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
if (buf->len != handlers[hdr->code].expect_len) {
|
|
LOG_ERR("Invalid len %u for code 0x%02x", buf->len, hdr->code);
|
|
smp_error(smp, BT_SMP_ERR_INVALID_PARAMS);
|
|
return 0;
|
|
}
|
|
|
|
err = handlers[hdr->code].func(smp, buf);
|
|
if (err) {
|
|
smp_error(smp, err);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void bt_smp_pkey_ready(const uint8_t *pkey)
|
|
{
|
|
int i;
|
|
|
|
LOG_DBG("");
|
|
|
|
sc_public_key = pkey;
|
|
|
|
if (!pkey) {
|
|
LOG_WRN("Public key not available");
|
|
return;
|
|
}
|
|
|
|
k_sem_give(&sc_local_pkey_ready);
|
|
|
|
for (i = 0; i < ARRAY_SIZE(bt_smp_pool); i++) {
|
|
struct bt_smp *smp = &bt_smp_pool[i];
|
|
uint8_t err;
|
|
|
|
if (!atomic_test_bit(smp->flags, SMP_FLAG_PKEY_SEND)) {
|
|
continue;
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_CENTRAL) &&
|
|
smp->chan.chan.conn->role == BT_HCI_ROLE_CENTRAL) {
|
|
err = sc_send_public_key(smp);
|
|
if (err) {
|
|
smp_error(smp, err);
|
|
}
|
|
|
|
atomic_set_bit(smp->allowed_cmds,
|
|
BT_SMP_CMD_PUBLIC_KEY);
|
|
continue;
|
|
}
|
|
|
|
#if defined(CONFIG_BT_PERIPHERAL)
|
|
err = smp_public_key_periph(smp);
|
|
if (err) {
|
|
smp_error(smp, err);
|
|
}
|
|
#endif /* CONFIG_BT_PERIPHERAL */
|
|
}
|
|
}
|
|
|
|
static void bt_smp_connected(struct bt_l2cap_chan *chan)
|
|
{
|
|
struct bt_smp *smp = CONTAINER_OF(chan, struct bt_smp, chan.chan);
|
|
|
|
LOG_DBG("chan %p cid 0x%04x", chan,
|
|
CONTAINER_OF(chan, struct bt_l2cap_le_chan, chan)->tx.cid);
|
|
|
|
k_work_init_delayable(&smp->work, smp_timeout);
|
|
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)
|
|
{
|
|
struct bt_smp *smp = CONTAINER_OF(chan, struct bt_smp, chan.chan);
|
|
struct bt_keys *keys = chan->conn->le.keys;
|
|
|
|
LOG_DBG("chan %p cid 0x%04x", chan,
|
|
CONTAINER_OF(chan, struct bt_l2cap_le_chan, chan)->tx.cid);
|
|
|
|
/* Channel disconnected callback is always called from a work handler
|
|
* so canceling of the timeout work should always succeed.
|
|
*/
|
|
(void)k_work_cancel_delayable(&smp->work);
|
|
|
|
if (atomic_test_bit(smp->flags, SMP_FLAG_PAIRING) ||
|
|
atomic_test_bit(smp->flags, SMP_FLAG_ENC_PENDING) ||
|
|
atomic_test_bit(smp->flags, SMP_FLAG_SEC_REQ)) {
|
|
/* reset context and report */
|
|
smp_pairing_complete(smp, BT_SMP_ERR_UNSPECIFIED);
|
|
}
|
|
|
|
if (keys) {
|
|
/*
|
|
* If debug keys were used for pairing remove them.
|
|
* No keys indicate no bonding so free keys storage.
|
|
*/
|
|
if (!keys->keys || (!IS_ENABLED(CONFIG_BT_STORE_DEBUG_KEYS) &&
|
|
(keys->flags & BT_KEYS_DEBUG))) {
|
|
bt_keys_clear(keys);
|
|
}
|
|
}
|
|
|
|
(void)memset(smp, 0, sizeof(*smp));
|
|
}
|
|
|
|
static void bt_smp_encrypt_change(struct bt_l2cap_chan *chan,
|
|
uint8_t hci_status)
|
|
{
|
|
struct bt_smp *smp = CONTAINER_OF(chan, struct bt_smp, chan.chan);
|
|
struct bt_conn *conn = chan->conn;
|
|
|
|
LOG_DBG("chan %p conn %p handle %u encrypt 0x%02x hci status 0x%02x", chan, conn,
|
|
conn->handle, conn->encrypt, hci_status);
|
|
|
|
if (!atomic_test_and_clear_bit(smp->flags, SMP_FLAG_ENC_PENDING)) {
|
|
/* We where not waiting for encryption procedure.
|
|
* This happens when encrypt change is called to notify that
|
|
* security has failed before starting encryption.
|
|
*/
|
|
return;
|
|
}
|
|
|
|
if (hci_status) {
|
|
if (atomic_test_bit(smp->flags, SMP_FLAG_PAIRING)) {
|
|
uint8_t smp_err = smp_err_get(
|
|
bt_security_err_get(hci_status));
|
|
|
|
/* Fail as if it happened during key distribution */
|
|
atomic_set_bit(smp->flags, SMP_FLAG_KEYS_DISTR);
|
|
smp_pairing_complete(smp, smp_err);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if (!conn->encrypt) {
|
|
return;
|
|
}
|
|
|
|
/* We were waiting for encryption but with no pairing in progress.
|
|
* This can happen if paired peripheral sent Security Request and we
|
|
* enabled encryption.
|
|
*/
|
|
if (!atomic_test_bit(smp->flags, SMP_FLAG_PAIRING)) {
|
|
smp_reset(smp);
|
|
return;
|
|
}
|
|
|
|
/* derive BR/EDR LinkKey if supported by both sides */
|
|
if (atomic_test_bit(smp->flags, SMP_FLAG_SC)) {
|
|
if ((smp->local_dist & BT_SMP_DIST_LINK_KEY) &&
|
|
(smp->remote_dist & BT_SMP_DIST_LINK_KEY)) {
|
|
/*
|
|
* Link Key will be derived after key distribution to
|
|
* make sure remote device identity is known
|
|
*/
|
|
atomic_set_bit(smp->flags, SMP_FLAG_DERIVE_LK);
|
|
}
|
|
/*
|
|
* Those are used as pairing finished indicator so generated
|
|
* but not distributed keys must be cleared here.
|
|
*/
|
|
smp->local_dist &= ~BT_SMP_DIST_LINK_KEY;
|
|
smp->remote_dist &= ~BT_SMP_DIST_LINK_KEY;
|
|
}
|
|
|
|
if (smp->remote_dist & BT_SMP_DIST_ENC_KEY) {
|
|
atomic_set_bit(smp->allowed_cmds, BT_SMP_CMD_ENCRYPT_INFO);
|
|
} else if (smp->remote_dist & BT_SMP_DIST_ID_KEY) {
|
|
atomic_set_bit(smp->allowed_cmds, BT_SMP_CMD_IDENT_INFO);
|
|
} else if (smp->remote_dist & BT_SMP_DIST_SIGN) {
|
|
atomic_set_bit(smp->allowed_cmds, BT_SMP_CMD_SIGNING_INFO);
|
|
}
|
|
|
|
/* This is the last point that is common for all code paths in the
|
|
* pairing process (during which we still have the option to send
|
|
* Pairing Failed). That makes it convenient to update the RL here. We
|
|
* want to update the RL during the pairing process so that we can fail
|
|
* it in case there is a conflict with an existing bond.
|
|
*
|
|
* We can do the update here only if the peer does not intend to send us
|
|
* any identity information. In this case we already have everything
|
|
* that goes into the RL.
|
|
*
|
|
* We need an entry in the RL despite the remote not using privacy. This
|
|
* is because we are using privacy locally and need to associate correct
|
|
* local IRK with the peer.
|
|
*
|
|
* If the peer does intend to send us identity information, we must wait
|
|
* for that information to enter it in the RL. In that case, we call
|
|
* `smp_id_add_replace` not here, but later. If neither we nor the peer
|
|
* are using privacy, there is no need for an entry in the RL.
|
|
*/
|
|
if (IS_ENABLED(CONFIG_BT_CENTRAL) &&
|
|
IS_ENABLED(CONFIG_BT_PRIVACY) &&
|
|
conn->role == BT_HCI_ROLE_CENTRAL &&
|
|
!(smp->remote_dist & BT_SMP_DIST_ID_KEY)) {
|
|
uint8_t smp_err;
|
|
|
|
smp_err = smp_id_add_replace(smp, conn->le.keys);
|
|
if (smp_err) {
|
|
smp_pairing_complete(smp, smp_err);
|
|
}
|
|
}
|
|
|
|
atomic_set_bit(smp->flags, SMP_FLAG_KEYS_DISTR);
|
|
|
|
/* Peripheral distributes it's keys first */
|
|
if (IS_ENABLED(CONFIG_BT_CENTRAL) &&
|
|
conn->role == BT_HCI_ROLE_CENTRAL && smp->remote_dist) {
|
|
return;
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_TESTING)) {
|
|
/* Avoid the HCI-USB race condition where HCI data and
|
|
* HCI events can be re-ordered, and pairing information appears
|
|
* to be sent unencrypted.
|
|
*/
|
|
k_sleep(K_MSEC(100));
|
|
}
|
|
|
|
if (bt_smp_distribute_keys(smp)) {
|
|
return;
|
|
}
|
|
|
|
/* if all keys were distributed, pairing is done */
|
|
if (!smp->local_dist && !smp->remote_dist) {
|
|
smp_pairing_complete(smp, 0);
|
|
}
|
|
}
|
|
|
|
#if defined(CONFIG_BT_SIGNING) || defined(CONFIG_BT_SMP_SELFTEST)
|
|
/* Sign message using msg as a buffer, len is a size of the message,
|
|
* msg buffer contains message itself, 32 bit count and signature,
|
|
* so total buffer size is len + 4 + 8 octets.
|
|
* API is Little Endian to make it suitable for Bluetooth.
|
|
*/
|
|
static int smp_sign_buf(const uint8_t *key, uint8_t *msg, uint16_t len)
|
|
{
|
|
uint8_t *m = msg;
|
|
uint32_t cnt = UNALIGNED_GET((uint32_t *)&msg[len]);
|
|
uint8_t *sig = msg + len;
|
|
uint8_t key_s[16], tmp[16];
|
|
int err;
|
|
|
|
LOG_DBG("Signing msg %s len %u key %s", bt_hex(msg, len), len, bt_hex(key, 16));
|
|
|
|
sys_mem_swap(m, len + sizeof(cnt));
|
|
sys_memcpy_swap(key_s, key, 16);
|
|
|
|
err = bt_crypto_aes_cmac(key_s, m, len + sizeof(cnt), tmp);
|
|
if (err) {
|
|
LOG_ERR("Data signing failed");
|
|
return err;
|
|
}
|
|
|
|
sys_mem_swap(tmp, sizeof(tmp));
|
|
memcpy(tmp + 4, &cnt, sizeof(cnt));
|
|
|
|
/* Swap original message back */
|
|
sys_mem_swap(m, len + sizeof(cnt));
|
|
|
|
memcpy(sig, tmp + 4, 12);
|
|
|
|
LOG_DBG("sig %s", bt_hex(sig, 12));
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
#if defined(CONFIG_BT_SIGNING)
|
|
int bt_smp_sign_verify(struct bt_conn *conn, struct net_buf *buf)
|
|
{
|
|
struct bt_keys *keys;
|
|
uint8_t sig[12];
|
|
uint32_t cnt;
|
|
int err;
|
|
|
|
/* Store signature incl. count */
|
|
memcpy(sig, net_buf_tail(buf) - sizeof(sig), sizeof(sig));
|
|
|
|
keys = bt_keys_find(BT_KEYS_REMOTE_CSRK, conn->id, &conn->le.dst);
|
|
if (!keys) {
|
|
LOG_ERR("Unable to find Remote CSRK for %s", bt_addr_le_str(&conn->le.dst));
|
|
return -ENOENT;
|
|
}
|
|
|
|
/* Copy signing count */
|
|
cnt = sys_cpu_to_le32(keys->remote_csrk.cnt);
|
|
memcpy(net_buf_tail(buf) - sizeof(sig), &cnt, sizeof(cnt));
|
|
|
|
LOG_DBG("Sign data len %zu key %s count %u", buf->len - sizeof(sig),
|
|
bt_hex(keys->remote_csrk.val, 16), keys->remote_csrk.cnt);
|
|
|
|
err = smp_sign_buf(keys->remote_csrk.val, buf->data,
|
|
buf->len - sizeof(sig));
|
|
if (err) {
|
|
LOG_ERR("Unable to create signature for %s", bt_addr_le_str(&conn->le.dst));
|
|
return -EIO;
|
|
}
|
|
|
|
if (memcmp(sig, net_buf_tail(buf) - sizeof(sig), sizeof(sig))) {
|
|
LOG_ERR("Unable to verify signature for %s", bt_addr_le_str(&conn->le.dst));
|
|
return -EBADMSG;
|
|
}
|
|
|
|
keys->remote_csrk.cnt++;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int bt_smp_sign(struct bt_conn *conn, struct net_buf *buf)
|
|
{
|
|
struct bt_keys *keys;
|
|
uint32_t cnt;
|
|
int err;
|
|
|
|
keys = bt_keys_find(BT_KEYS_LOCAL_CSRK, conn->id, &conn->le.dst);
|
|
if (!keys) {
|
|
LOG_ERR("Unable to find local CSRK for %s", bt_addr_le_str(&conn->le.dst));
|
|
return -ENOENT;
|
|
}
|
|
|
|
/* Reserve space for data signature */
|
|
net_buf_add(buf, 12);
|
|
|
|
/* Copy signing count */
|
|
cnt = sys_cpu_to_le32(keys->local_csrk.cnt);
|
|
memcpy(net_buf_tail(buf) - 12, &cnt, sizeof(cnt));
|
|
|
|
LOG_DBG("Sign data len %u key %s count %u", buf->len, bt_hex(keys->local_csrk.val, 16),
|
|
keys->local_csrk.cnt);
|
|
|
|
err = smp_sign_buf(keys->local_csrk.val, buf->data, buf->len - 12);
|
|
if (err) {
|
|
LOG_ERR("Unable to create signature for %s", bt_addr_le_str(&conn->le.dst));
|
|
return -EIO;
|
|
}
|
|
|
|
keys->local_csrk.cnt++;
|
|
|
|
return 0;
|
|
}
|
|
#else
|
|
int bt_smp_sign_verify(struct bt_conn *conn, struct net_buf *buf)
|
|
{
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
int bt_smp_sign(struct bt_conn *conn, struct net_buf *buf)
|
|
{
|
|
return -ENOTSUP;
|
|
}
|
|
#endif /* CONFIG_BT_SIGNING */
|
|
|
|
static int smp_d1(const uint8_t *key, uint16_t d, uint16_t r, uint8_t res[16])
|
|
{
|
|
int err;
|
|
|
|
LOG_DBG("key %s d %u r %u", bt_hex(key, 16), d, r);
|
|
|
|
sys_put_le16(d, &res[0]);
|
|
sys_put_le16(r, &res[2]);
|
|
memset(&res[4], 0, 16 - 4);
|
|
|
|
err = bt_encrypt_le(key, res, res);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
LOG_DBG("res %s", bt_hex(res, 16));
|
|
return 0;
|
|
}
|
|
|
|
int bt_smp_irk_get(uint8_t *ir, uint8_t *irk)
|
|
{
|
|
uint8_t invalid_ir[16] = { 0 };
|
|
|
|
if (!memcmp(ir, invalid_ir, 16)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
return smp_d1(ir, 1, 0, irk);
|
|
}
|
|
|
|
#if defined(CONFIG_BT_SMP_SELFTEST)
|
|
/* Test vectors are taken from RFC 4493
|
|
* https://tools.ietf.org/html/rfc4493
|
|
* Same mentioned in the Bluetooth Spec.
|
|
*/
|
|
static const uint8_t key[] = {
|
|
0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
|
|
0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c
|
|
};
|
|
|
|
static const uint8_t M[] = {
|
|
0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
|
|
0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
|
|
0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c,
|
|
0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
|
|
0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11,
|
|
0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
|
|
0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17,
|
|
0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10
|
|
};
|
|
|
|
static int aes_test(const char *prefix, const uint8_t *in_key, const uint8_t *m,
|
|
uint16_t len, const uint8_t *mac)
|
|
{
|
|
uint8_t out[16];
|
|
|
|
LOG_DBG("%s: AES CMAC of message with len %u", prefix, len);
|
|
|
|
bt_crypto_aes_cmac(in_key, m, len, out);
|
|
if (!memcmp(out, mac, 16)) {
|
|
LOG_DBG("%s: Success", prefix);
|
|
} else {
|
|
LOG_ERR("%s: Failed", prefix);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int smp_aes_cmac_test(void)
|
|
{
|
|
uint8_t mac1[] = {
|
|
0xbb, 0x1d, 0x69, 0x29, 0xe9, 0x59, 0x37, 0x28,
|
|
0x7f, 0xa3, 0x7d, 0x12, 0x9b, 0x75, 0x67, 0x46
|
|
};
|
|
uint8_t mac2[] = {
|
|
0x07, 0x0a, 0x16, 0xb4, 0x6b, 0x4d, 0x41, 0x44,
|
|
0xf7, 0x9b, 0xdd, 0x9d, 0xd0, 0x4a, 0x28, 0x7c
|
|
};
|
|
uint8_t mac3[] = {
|
|
0xdf, 0xa6, 0x67, 0x47, 0xde, 0x9a, 0xe6, 0x30,
|
|
0x30, 0xca, 0x32, 0x61, 0x14, 0x97, 0xc8, 0x27
|
|
};
|
|
uint8_t mac4[] = {
|
|
0x51, 0xf0, 0xbe, 0xbf, 0x7e, 0x3b, 0x9d, 0x92,
|
|
0xfc, 0x49, 0x74, 0x17, 0x79, 0x36, 0x3c, 0xfe
|
|
};
|
|
int err;
|
|
|
|
err = aes_test("Test aes-cmac0", key, M, 0, mac1);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
err = aes_test("Test aes-cmac16", key, M, 16, mac2);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
err = aes_test("Test aes-cmac40", key, M, 40, mac3);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
err = aes_test("Test aes-cmac64", key, M, 64, mac4);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int sign_test(const char *prefix, const uint8_t *sign_key, const uint8_t *m,
|
|
uint16_t len, const uint8_t *sig)
|
|
{
|
|
uint8_t msg[len + sizeof(uint32_t) + 8];
|
|
uint8_t orig[len + sizeof(uint32_t) + 8];
|
|
uint8_t *out = msg + len;
|
|
int err;
|
|
|
|
LOG_DBG("%s: Sign message with len %u", prefix, len);
|
|
|
|
(void)memset(msg, 0, sizeof(msg));
|
|
memcpy(msg, m, len);
|
|
(void)memset(msg + len, 0, sizeof(uint32_t));
|
|
|
|
memcpy(orig, msg, sizeof(msg));
|
|
|
|
err = smp_sign_buf(sign_key, msg, len);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
/* Check original message */
|
|
if (!memcmp(msg, orig, len + sizeof(uint32_t))) {
|
|
LOG_DBG("%s: Original message intact", prefix);
|
|
} else {
|
|
LOG_ERR("%s: Original message modified", prefix);
|
|
LOG_DBG("%s: orig %s", prefix, bt_hex(orig, sizeof(orig)));
|
|
LOG_DBG("%s: msg %s", prefix, bt_hex(msg, sizeof(msg)));
|
|
return -1;
|
|
}
|
|
|
|
if (!memcmp(out, sig, 12)) {
|
|
LOG_DBG("%s: Success", prefix);
|
|
} else {
|
|
LOG_ERR("%s: Failed", prefix);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int smp_sign_test(void)
|
|
{
|
|
const uint8_t sig1[] = {
|
|
0x00, 0x00, 0x00, 0x00, 0xb3, 0xa8, 0x59, 0x41,
|
|
0x27, 0xeb, 0xc2, 0xc0
|
|
};
|
|
const uint8_t sig2[] = {
|
|
0x00, 0x00, 0x00, 0x00, 0x27, 0x39, 0x74, 0xf4,
|
|
0x39, 0x2a, 0x23, 0x2a
|
|
};
|
|
const uint8_t sig3[] = {
|
|
0x00, 0x00, 0x00, 0x00, 0xb7, 0xca, 0x94, 0xab,
|
|
0x87, 0xc7, 0x82, 0x18
|
|
};
|
|
const uint8_t sig4[] = {
|
|
0x00, 0x00, 0x00, 0x00, 0x44, 0xe1, 0xe6, 0xce,
|
|
0x1d, 0xf5, 0x13, 0x68
|
|
};
|
|
uint8_t key_s[16];
|
|
int err;
|
|
|
|
/* Use the same key as aes-cmac but swap bytes */
|
|
sys_memcpy_swap(key_s, key, 16);
|
|
|
|
err = sign_test("Test sign0", key_s, M, 0, sig1);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
err = sign_test("Test sign16", key_s, M, 16, sig2);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
err = sign_test("Test sign40", key_s, M, 40, sig3);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
err = sign_test("Test sign64", key_s, M, 64, sig4);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int smp_f4_test(void)
|
|
{
|
|
uint8_t u[32] = { 0xe6, 0x9d, 0x35, 0x0e, 0x48, 0x01, 0x03, 0xcc,
|
|
0xdb, 0xfd, 0xf4, 0xac, 0x11, 0x91, 0xf4, 0xef,
|
|
0xb9, 0xa5, 0xf9, 0xe9, 0xa7, 0x83, 0x2c, 0x5e,
|
|
0x2c, 0xbe, 0x97, 0xf2, 0xd2, 0x03, 0xb0, 0x20 };
|
|
uint8_t v[32] = { 0xfd, 0xc5, 0x7f, 0xf4, 0x49, 0xdd, 0x4f, 0x6b,
|
|
0xfb, 0x7c, 0x9d, 0xf1, 0xc2, 0x9a, 0xcb, 0x59,
|
|
0x2a, 0xe7, 0xd4, 0xee, 0xfb, 0xfc, 0x0a, 0x90,
|
|
0x9a, 0xbb, 0xf6, 0x32, 0x3d, 0x8b, 0x18, 0x55 };
|
|
uint8_t x[16] = { 0xab, 0xae, 0x2b, 0x71, 0xec, 0xb2, 0xff, 0xff,
|
|
0x3e, 0x73, 0x77, 0xd1, 0x54, 0x84, 0xcb, 0xd5 };
|
|
uint8_t z = 0x00;
|
|
uint8_t exp[16] = { 0x2d, 0x87, 0x74, 0xa9, 0xbe, 0xa1, 0xed, 0xf1,
|
|
0x1c, 0xbd, 0xa9, 0x07, 0xf1, 0x16, 0xc9, 0xf2 };
|
|
uint8_t res[16];
|
|
int err;
|
|
|
|
err = bt_crypto_f4(u, v, x, z, res);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
if (memcmp(res, exp, 16)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int smp_f5_test(void)
|
|
{
|
|
uint8_t w[32] = { 0x98, 0xa6, 0xbf, 0x73, 0xf3, 0x34, 0x8d, 0x86,
|
|
0xf1, 0x66, 0xf8, 0xb4, 0x13, 0x6b, 0x79, 0x99,
|
|
0x9b, 0x7d, 0x39, 0x0a, 0xa6, 0x10, 0x10, 0x34,
|
|
0x05, 0xad, 0xc8, 0x57, 0xa3, 0x34, 0x02, 0xec };
|
|
uint8_t n1[16] = { 0xab, 0xae, 0x2b, 0x71, 0xec, 0xb2, 0xff, 0xff,
|
|
0x3e, 0x73, 0x77, 0xd1, 0x54, 0x84, 0xcb, 0xd5 };
|
|
uint8_t n2[16] = { 0xcf, 0xc4, 0x3d, 0xff, 0xf7, 0x83, 0x65, 0x21,
|
|
0x6e, 0x5f, 0xa7, 0x25, 0xcc, 0xe7, 0xe8, 0xa6 };
|
|
bt_addr_le_t a1 = { .type = 0x00,
|
|
.a.val = { 0xce, 0xbf, 0x37, 0x37, 0x12, 0x56 } };
|
|
bt_addr_le_t a2 = { .type = 0x00,
|
|
.a.val = {0xc1, 0xcf, 0x2d, 0x70, 0x13, 0xa7 } };
|
|
uint8_t exp_ltk[16] = { 0x38, 0x0a, 0x75, 0x94, 0xb5, 0x22, 0x05,
|
|
0x98, 0x23, 0xcd, 0xd7, 0x69, 0x11, 0x79,
|
|
0x86, 0x69 };
|
|
uint8_t exp_mackey[16] = { 0x20, 0x6e, 0x63, 0xce, 0x20, 0x6a, 0x3f,
|
|
0xfd, 0x02, 0x4a, 0x08, 0xa1, 0x76, 0xf1,
|
|
0x65, 0x29 };
|
|
uint8_t mackey[16], ltk[16];
|
|
int err;
|
|
|
|
err = bt_crypto_f5(w, n1, n2, &a1, &a2, mackey, ltk);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
if (memcmp(mackey, exp_mackey, 16) || memcmp(ltk, exp_ltk, 16)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int smp_f6_test(void)
|
|
{
|
|
uint8_t w[16] = { 0x20, 0x6e, 0x63, 0xce, 0x20, 0x6a, 0x3f, 0xfd,
|
|
0x02, 0x4a, 0x08, 0xa1, 0x76, 0xf1, 0x65, 0x29 };
|
|
uint8_t n1[16] = { 0xab, 0xae, 0x2b, 0x71, 0xec, 0xb2, 0xff, 0xff,
|
|
0x3e, 0x73, 0x77, 0xd1, 0x54, 0x84, 0xcb, 0xd5 };
|
|
uint8_t n2[16] = { 0xcf, 0xc4, 0x3d, 0xff, 0xf7, 0x83, 0x65, 0x21,
|
|
0x6e, 0x5f, 0xa7, 0x25, 0xcc, 0xe7, 0xe8, 0xa6 };
|
|
uint8_t r[16] = { 0xc8, 0x0f, 0x2d, 0x0c, 0xd2, 0x42, 0xda, 0x08,
|
|
0x54, 0xbb, 0x53, 0xb4, 0x3b, 0x34, 0xa3, 0x12 };
|
|
uint8_t io_cap[3] = { 0x02, 0x01, 0x01 };
|
|
bt_addr_le_t a1 = { .type = 0x00,
|
|
.a.val = { 0xce, 0xbf, 0x37, 0x37, 0x12, 0x56 } };
|
|
bt_addr_le_t a2 = { .type = 0x00,
|
|
.a.val = {0xc1, 0xcf, 0x2d, 0x70, 0x13, 0xa7 } };
|
|
uint8_t exp[16] = { 0x61, 0x8f, 0x95, 0xda, 0x09, 0x0b, 0x6c, 0xd2,
|
|
0xc5, 0xe8, 0xd0, 0x9c, 0x98, 0x73, 0xc4, 0xe3 };
|
|
uint8_t res[16];
|
|
int err;
|
|
|
|
err = bt_crypto_f6(w, n1, n2, r, io_cap, &a1, &a2, res);
|
|
if (err)
|
|
return err;
|
|
|
|
if (memcmp(res, exp, 16))
|
|
return -EINVAL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int smp_g2_test(void)
|
|
{
|
|
uint8_t u[32] = { 0xe6, 0x9d, 0x35, 0x0e, 0x48, 0x01, 0x03, 0xcc,
|
|
0xdb, 0xfd, 0xf4, 0xac, 0x11, 0x91, 0xf4, 0xef,
|
|
0xb9, 0xa5, 0xf9, 0xe9, 0xa7, 0x83, 0x2c, 0x5e,
|
|
0x2c, 0xbe, 0x97, 0xf2, 0xd2, 0x03, 0xb0, 0x20 };
|
|
uint8_t v[32] = { 0xfd, 0xc5, 0x7f, 0xf4, 0x49, 0xdd, 0x4f, 0x6b,
|
|
0xfb, 0x7c, 0x9d, 0xf1, 0xc2, 0x9a, 0xcb, 0x59,
|
|
0x2a, 0xe7, 0xd4, 0xee, 0xfb, 0xfc, 0x0a, 0x90,
|
|
0x9a, 0xbb, 0xf6, 0x32, 0x3d, 0x8b, 0x18, 0x55 };
|
|
uint8_t x[16] = { 0xab, 0xae, 0x2b, 0x71, 0xec, 0xb2, 0xff, 0xff,
|
|
0x3e, 0x73, 0x77, 0xd1, 0x54, 0x84, 0xcb, 0xd5 };
|
|
uint8_t y[16] = { 0xcf, 0xc4, 0x3d, 0xff, 0xf7, 0x83, 0x65, 0x21,
|
|
0x6e, 0x5f, 0xa7, 0x25, 0xcc, 0xe7, 0xe8, 0xa6 };
|
|
uint32_t exp_val = 0x2f9ed5ba % 1000000;
|
|
uint32_t val;
|
|
int err;
|
|
|
|
err = bt_crypto_g2(u, v, x, y, &val);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
if (val != exp_val) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if defined(CONFIG_BT_BREDR)
|
|
static int smp_h6_test(void)
|
|
{
|
|
uint8_t w[16] = { 0x9b, 0x7d, 0x39, 0x0a, 0xa6, 0x10, 0x10, 0x34,
|
|
0x05, 0xad, 0xc8, 0x57, 0xa3, 0x34, 0x02, 0xec };
|
|
uint8_t key_id[4] = { 0x72, 0x62, 0x65, 0x6c };
|
|
uint8_t exp_res[16] = { 0x99, 0x63, 0xb1, 0x80, 0xe2, 0xa9, 0xd3, 0xe8,
|
|
0x1c, 0xc9, 0x6d, 0xe7, 0x02, 0xe1, 0x9a, 0x2d};
|
|
uint8_t res[16];
|
|
int err;
|
|
|
|
err = bt_crypto_h6(w, key_id, res);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
if (memcmp(res, exp_res, 16)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int smp_h7_test(void)
|
|
{
|
|
uint8_t salt[16] = { 0x31, 0x70, 0x6d, 0x74, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
|
uint8_t w[16] = { 0x9b, 0x7d, 0x39, 0x0a, 0xa6, 0x10, 0x10, 0x34,
|
|
0x05, 0xad, 0xc8, 0x57, 0xa3, 0x34, 0x02, 0xec };
|
|
uint8_t exp_res[16] = { 0x11, 0x70, 0xa5, 0x75, 0x2a, 0x8c, 0x99, 0xd2,
|
|
0xec, 0xc0, 0xa3, 0xc6, 0x97, 0x35, 0x17, 0xfb};
|
|
uint8_t res[16];
|
|
int err;
|
|
|
|
err = bt_crypto_h7(salt, w, res);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
if (memcmp(res, exp_res, 16)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif /* CONFIG_BT_BREDR */
|
|
|
|
static int smp_h8_test(void)
|
|
{
|
|
uint8_t k[16] = {0xec, 0x02, 0x34, 0xa3, 0x57, 0xc8, 0xad, 0x05,
|
|
0x34, 0x10, 0x10, 0xa6, 0x0a, 0x39, 0x7d, 0x9b};
|
|
uint8_t s[16] = {0x15, 0x36, 0xd1, 0x8d, 0xe3, 0xd2, 0x0d, 0xf9,
|
|
0x9b, 0x70, 0x44, 0xc1, 0x2f, 0x9e, 0xd5, 0xba};
|
|
uint8_t key_id[4] = {0xcc, 0x03, 0x01, 0x48};
|
|
|
|
uint8_t exp_res[16] = {0xe5, 0xe5, 0xbe, 0xba, 0xae, 0x72, 0x28, 0xe7,
|
|
0x22, 0xa3, 0x89, 0x04, 0xed, 0x35, 0x0f, 0x6d};
|
|
uint8_t res[16];
|
|
int err;
|
|
|
|
err = bt_crypto_h8(k, s, key_id, res);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
if (memcmp(res, exp_res, 16)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int smp_self_test(void)
|
|
{
|
|
int err;
|
|
|
|
err = smp_aes_cmac_test();
|
|
if (err) {
|
|
LOG_ERR("SMP AES-CMAC self tests failed");
|
|
return err;
|
|
}
|
|
|
|
err = smp_sign_test();
|
|
if (err) {
|
|
LOG_ERR("SMP signing self tests failed");
|
|
return err;
|
|
}
|
|
|
|
err = smp_f4_test();
|
|
if (err) {
|
|
LOG_ERR("SMP f4 self test failed");
|
|
return err;
|
|
}
|
|
|
|
err = smp_f5_test();
|
|
if (err) {
|
|
LOG_ERR("SMP f5 self test failed");
|
|
return err;
|
|
}
|
|
|
|
err = smp_f6_test();
|
|
if (err) {
|
|
LOG_ERR("SMP f6 self test failed");
|
|
return err;
|
|
}
|
|
|
|
err = smp_g2_test();
|
|
if (err) {
|
|
LOG_ERR("SMP g2 self test failed");
|
|
return err;
|
|
}
|
|
|
|
#if defined(CONFIG_BT_BREDR)
|
|
err = smp_h6_test();
|
|
if (err) {
|
|
LOG_ERR("SMP h6 self test failed");
|
|
return err;
|
|
}
|
|
|
|
err = smp_h7_test();
|
|
if (err) {
|
|
LOG_ERR("SMP h7 self test failed");
|
|
return err;
|
|
}
|
|
#endif /* CONFIG_BT_BREDR */
|
|
err = smp_h8_test();
|
|
if (err) {
|
|
LOG_ERR("SMP h8 self test failed");
|
|
return err;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#else
|
|
static inline int smp_self_test(void)
|
|
{
|
|
return 0;
|
|
}
|
|
#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;
|
|
|
|
smp = smp_chan_get(conn);
|
|
if (!smp) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (atomic_ptr_cas(&smp->auth_cb, BT_SMP_AUTH_CB_UNINITIALIZED, (atomic_ptr_val_t)cb)) {
|
|
return 0;
|
|
} else {
|
|
return -EALREADY;
|
|
}
|
|
}
|
|
|
|
#if defined(CONFIG_BT_PASSKEY_KEYPRESS)
|
|
static int smp_send_keypress_notif(struct bt_smp *smp, uint8_t type)
|
|
{
|
|
struct bt_smp_keypress_notif *req;
|
|
struct net_buf *buf;
|
|
|
|
buf = smp_create_pdu(smp, BT_SMP_KEYPRESS_NOTIFICATION, sizeof(*req));
|
|
if (!buf) {
|
|
return -ENOMEM;
|
|
}
|
|
|
|
req = net_buf_add(buf, sizeof(*req));
|
|
req->type = type;
|
|
|
|
smp_send(smp, buf, NULL, NULL);
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
#if defined(CONFIG_BT_PASSKEY_KEYPRESS)
|
|
int bt_smp_auth_keypress_notify(struct bt_conn *conn, enum bt_conn_auth_keypress type)
|
|
{
|
|
struct bt_smp *smp;
|
|
|
|
smp = smp_chan_get(conn);
|
|
if (!smp) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
CHECKIF(!IN_RANGE(type,
|
|
BT_CONN_AUTH_KEYPRESS_ENTRY_STARTED,
|
|
BT_CONN_AUTH_KEYPRESS_ENTRY_COMPLETED)) {
|
|
LOG_ERR("Refusing to send unknown event type %u", type);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (smp->method != PASSKEY_INPUT ||
|
|
!atomic_test_bit(smp->flags, SMP_FLAG_USER)) {
|
|
LOG_ERR("Refusing to send keypress: Not waiting for passkey input.");
|
|
return -EINVAL;
|
|
}
|
|
|
|
return smp_send_keypress_notif(smp, type);
|
|
}
|
|
#endif
|
|
|
|
int bt_smp_auth_passkey_entry(struct bt_conn *conn, unsigned int passkey)
|
|
{
|
|
struct bt_smp *smp;
|
|
uint8_t err;
|
|
|
|
smp = smp_chan_get(conn);
|
|
if (!smp) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!atomic_test_and_clear_bit(smp->flags, SMP_FLAG_USER)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
#if !defined(CONFIG_BT_SMP_SC_PAIR_ONLY)
|
|
if (!atomic_test_bit(smp->flags, SMP_FLAG_SC)) {
|
|
legacy_passkey_entry(smp, passkey);
|
|
return 0;
|
|
}
|
|
#endif /* !CONFIG_BT_SMP_SC_PAIR_ONLY */
|
|
|
|
smp->passkey = sys_cpu_to_le32(passkey);
|
|
|
|
if (IS_ENABLED(CONFIG_BT_CENTRAL) &&
|
|
smp->chan.chan.conn->role == BT_HCI_ROLE_CENTRAL) {
|
|
atomic_set_bit(smp->allowed_cmds, BT_SMP_CMD_PAIRING_CONFIRM);
|
|
err = smp_send_pairing_confirm(smp);
|
|
if (err) {
|
|
smp_error(smp, BT_SMP_ERR_PASSKEY_ENTRY_FAILED);
|
|
return 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_PERIPHERAL) &&
|
|
atomic_test_bit(smp->flags, SMP_FLAG_CFM_DELAYED)) {
|
|
atomic_set_bit(smp->allowed_cmds, BT_SMP_CMD_PAIRING_RANDOM);
|
|
err = smp_send_pairing_confirm(smp);
|
|
if (err) {
|
|
smp_error(smp, BT_SMP_ERR_PASSKEY_ENTRY_FAILED);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int bt_smp_auth_passkey_confirm(struct bt_conn *conn)
|
|
{
|
|
struct bt_smp *smp;
|
|
|
|
smp = smp_chan_get(conn);
|
|
if (!smp) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!atomic_test_and_clear_bit(smp->flags, SMP_FLAG_USER)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* wait for DHKey being generated */
|
|
if (atomic_test_bit(smp->flags, SMP_FLAG_DHKEY_PENDING)) {
|
|
atomic_set_bit(smp->flags, SMP_FLAG_DHKEY_SEND);
|
|
return 0;
|
|
}
|
|
|
|
/* wait for remote DHKey Check */
|
|
if (atomic_test_bit(smp->flags, SMP_FLAG_DHCHECK_WAIT)) {
|
|
atomic_set_bit(smp->flags, SMP_FLAG_DHKEY_SEND);
|
|
return 0;
|
|
}
|
|
|
|
if (atomic_test_bit(smp->flags, SMP_FLAG_DHKEY_SEND)) {
|
|
uint8_t err;
|
|
|
|
#if defined(CONFIG_BT_CENTRAL)
|
|
if (smp->chan.chan.conn->role == BT_HCI_ROLE_CENTRAL) {
|
|
err = compute_and_send_central_dhcheck(smp);
|
|
if (err) {
|
|
smp_error(smp, err);
|
|
}
|
|
return 0;
|
|
}
|
|
#endif /* CONFIG_BT_CENTRAL */
|
|
|
|
#if defined(CONFIG_BT_PERIPHERAL)
|
|
err = compute_and_check_and_send_periph_dhcheck(smp);
|
|
if (err) {
|
|
smp_error(smp, err);
|
|
}
|
|
#endif /* CONFIG_BT_PERIPHERAL */
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if !defined(CONFIG_BT_SMP_SC_PAIR_ONLY)
|
|
int bt_smp_le_oob_set_tk(struct bt_conn *conn, const uint8_t *tk)
|
|
{
|
|
struct bt_smp *smp;
|
|
|
|
smp = smp_chan_get(conn);
|
|
if (!smp || !tk) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
LOG_DBG("%s", bt_hex(tk, 16));
|
|
|
|
if (!atomic_test_and_clear_bit(smp->flags, SMP_FLAG_USER)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_LOG_SNIFFER_INFO)) {
|
|
uint8_t oob[16];
|
|
|
|
sys_memcpy_swap(oob, tk, 16);
|
|
LOG_INF("Legacy OOB data 0x%s", bt_hex(oob, 16));
|
|
}
|
|
|
|
memcpy(smp->tk, tk, 16*sizeof(uint8_t));
|
|
|
|
legacy_user_tk_entry(smp);
|
|
|
|
return 0;
|
|
}
|
|
#endif /* !defined(CONFIG_BT_SMP_SC_PAIR_ONLY) */
|
|
|
|
int bt_smp_le_oob_generate_sc_data(struct bt_le_oob_sc_data *le_sc_oob)
|
|
{
|
|
int err;
|
|
|
|
if (!le_sc_supported()) {
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
if (!sc_public_key) {
|
|
err = k_sem_take(&sc_local_pkey_ready, K_FOREVER);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_OOB_DATA_FIXED)) {
|
|
uint8_t rand_num[] = {
|
|
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
|
|
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
|
|
};
|
|
|
|
memcpy(le_sc_oob->r, rand_num, sizeof(le_sc_oob->r));
|
|
} else {
|
|
err = bt_rand(le_sc_oob->r, 16);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
}
|
|
|
|
err = bt_crypto_f4(sc_public_key, sc_public_key, le_sc_oob->r, 0,
|
|
le_sc_oob->c);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if !defined(CONFIG_BT_SMP_OOB_LEGACY_PAIR_ONLY)
|
|
static bool le_sc_oob_data_check(struct bt_smp *smp, bool oobd_local_present,
|
|
bool oobd_remote_present)
|
|
{
|
|
bool req_oob_present = le_sc_oob_data_req_check(smp);
|
|
bool rsp_oob_present = le_sc_oob_data_rsp_check(smp);
|
|
|
|
if (IS_ENABLED(CONFIG_BT_CENTRAL) &&
|
|
smp->chan.chan.conn->role == BT_HCI_ROLE_CENTRAL) {
|
|
if ((req_oob_present != oobd_remote_present) &&
|
|
(rsp_oob_present != oobd_local_present)) {
|
|
return false;
|
|
}
|
|
} else if (IS_ENABLED(CONFIG_BT_PERIPHERAL)) {
|
|
if ((req_oob_present != oobd_local_present) &&
|
|
(rsp_oob_present != oobd_remote_present)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static int le_sc_oob_pairing_continue(struct bt_smp *smp)
|
|
{
|
|
if (smp->oobd_remote) {
|
|
int err;
|
|
uint8_t c[16];
|
|
|
|
err = bt_crypto_f4(smp->pkey, smp->pkey, smp->oobd_remote->r, 0, c);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
bool match = (memcmp(c, smp->oobd_remote->c, sizeof(c)) == 0);
|
|
|
|
if (!match) {
|
|
smp_error(smp, BT_SMP_ERR_CONFIRM_FAILED);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_CENTRAL) &&
|
|
smp->chan.chan.conn->role == BT_HCI_ROLE_CENTRAL) {
|
|
atomic_set_bit(smp->allowed_cmds, BT_SMP_CMD_PAIRING_RANDOM);
|
|
} else if (IS_ENABLED(CONFIG_BT_PERIPHERAL)) {
|
|
atomic_set_bit(smp->allowed_cmds, BT_SMP_DHKEY_CHECK);
|
|
atomic_set_bit(smp->flags, SMP_FLAG_DHCHECK_WAIT);
|
|
}
|
|
|
|
return smp_send_pairing_random(smp);
|
|
}
|
|
|
|
int bt_smp_le_oob_set_sc_data(struct bt_conn *conn,
|
|
const struct bt_le_oob_sc_data *oobd_local,
|
|
const struct bt_le_oob_sc_data *oobd_remote)
|
|
{
|
|
struct bt_smp *smp;
|
|
|
|
smp = smp_chan_get(conn);
|
|
if (!smp) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!le_sc_oob_data_check(smp, (oobd_local != NULL),
|
|
(oobd_remote != NULL))) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!atomic_test_and_clear_bit(smp->flags, SMP_FLAG_OOB_PENDING)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
smp->oobd_local = oobd_local;
|
|
smp->oobd_remote = oobd_remote;
|
|
|
|
return le_sc_oob_pairing_continue(smp);
|
|
}
|
|
|
|
int bt_smp_le_oob_get_sc_data(struct bt_conn *conn,
|
|
const struct bt_le_oob_sc_data **oobd_local,
|
|
const struct bt_le_oob_sc_data **oobd_remote)
|
|
{
|
|
struct bt_smp *smp;
|
|
|
|
smp = smp_chan_get(conn);
|
|
if (!smp) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!smp->oobd_local && !smp->oobd_remote) {
|
|
return -ESRCH;
|
|
}
|
|
|
|
if (oobd_local) {
|
|
*oobd_local = smp->oobd_local;
|
|
}
|
|
|
|
if (oobd_remote) {
|
|
*oobd_remote = smp->oobd_remote;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif /* !CONFIG_BT_SMP_OOB_LEGACY_PAIR_ONLY */
|
|
|
|
int bt_smp_auth_cancel(struct bt_conn *conn)
|
|
{
|
|
struct bt_smp *smp;
|
|
|
|
smp = smp_chan_get(conn);
|
|
if (!smp) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!atomic_test_and_clear_bit(smp->flags, SMP_FLAG_USER)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
LOG_DBG("");
|
|
|
|
switch (smp->method) {
|
|
case PASSKEY_INPUT:
|
|
case PASSKEY_DISPLAY:
|
|
return smp_error(smp, BT_SMP_ERR_PASSKEY_ENTRY_FAILED);
|
|
case PASSKEY_CONFIRM:
|
|
return smp_error(smp, BT_SMP_ERR_CONFIRM_FAILED);
|
|
case LE_SC_OOB:
|
|
case LEGACY_OOB:
|
|
return smp_error(smp, BT_SMP_ERR_OOB_NOT_AVAIL);
|
|
case JUST_WORKS:
|
|
return smp_error(smp, BT_SMP_ERR_UNSPECIFIED);
|
|
default:
|
|
LOG_ERR("Unknown pairing method (%u)", smp->method);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
#if !defined(CONFIG_BT_SMP_SC_PAIR_ONLY)
|
|
int bt_smp_auth_pairing_confirm(struct bt_conn *conn)
|
|
{
|
|
struct bt_smp *smp;
|
|
|
|
smp = smp_chan_get(conn);
|
|
if (!smp) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!atomic_test_and_clear_bit(smp->flags, SMP_FLAG_USER)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_CENTRAL) &&
|
|
conn->role == BT_CONN_ROLE_CENTRAL) {
|
|
if (!atomic_test_bit(smp->flags, SMP_FLAG_SC)) {
|
|
atomic_set_bit(smp->allowed_cmds,
|
|
BT_SMP_CMD_PAIRING_CONFIRM);
|
|
return legacy_send_pairing_confirm(smp);
|
|
}
|
|
|
|
if (!sc_public_key) {
|
|
atomic_set_bit(smp->flags, SMP_FLAG_PKEY_SEND);
|
|
return 0;
|
|
}
|
|
|
|
atomic_set_bit(smp->allowed_cmds, BT_SMP_CMD_PUBLIC_KEY);
|
|
return sc_send_public_key(smp);
|
|
}
|
|
|
|
#if defined(CONFIG_BT_PERIPHERAL)
|
|
if (!atomic_test_bit(smp->flags, SMP_FLAG_SC)) {
|
|
atomic_set_bit(smp->allowed_cmds,
|
|
BT_SMP_CMD_PAIRING_CONFIRM);
|
|
return send_pairing_rsp(smp);
|
|
}
|
|
|
|
atomic_set_bit(smp->allowed_cmds, BT_SMP_CMD_PUBLIC_KEY);
|
|
if (send_pairing_rsp(smp)) {
|
|
return -EIO;
|
|
}
|
|
#endif /* CONFIG_BT_PERIPHERAL */
|
|
|
|
return 0;
|
|
}
|
|
#else
|
|
int bt_smp_auth_pairing_confirm(struct bt_conn *conn)
|
|
{
|
|
/* confirm_pairing will never be called in LE SC only mode */
|
|
return -EINVAL;
|
|
}
|
|
#endif /* !CONFIG_BT_SMP_SC_PAIR_ONLY */
|
|
|
|
#if defined(CONFIG_BT_FIXED_PASSKEY)
|
|
int bt_passkey_set(unsigned int passkey)
|
|
{
|
|
if (passkey == BT_PASSKEY_INVALID) {
|
|
fixed_passkey = BT_PASSKEY_INVALID;
|
|
return 0;
|
|
}
|
|
|
|
if (passkey > 999999) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
fixed_passkey = passkey;
|
|
return 0;
|
|
}
|
|
#endif /* CONFIG_BT_FIXED_PASSKEY */
|
|
|
|
int bt_smp_start_security(struct bt_conn *conn)
|
|
{
|
|
switch (conn->role) {
|
|
#if defined(CONFIG_BT_CENTRAL)
|
|
case BT_HCI_ROLE_CENTRAL:
|
|
{
|
|
int err;
|
|
struct bt_smp *smp;
|
|
|
|
smp = smp_chan_get(conn);
|
|
if (!smp) {
|
|
return -ENOTCONN;
|
|
}
|
|
|
|
/* pairing is in progress */
|
|
if (atomic_test_bit(smp->flags, SMP_FLAG_PAIRING)) {
|
|
return -EBUSY;
|
|
}
|
|
|
|
/* Encryption is in progress */
|
|
if (atomic_test_bit(smp->flags, SMP_FLAG_ENC_PENDING)) {
|
|
return -EBUSY;
|
|
}
|
|
|
|
if (!smp_keys_check(conn)) {
|
|
return smp_send_pairing_req(conn);
|
|
}
|
|
|
|
/* LE SC LTK and legacy central LTK are stored in same place */
|
|
err = bt_conn_le_start_encryption(conn,
|
|
conn->le.keys->ltk.rand,
|
|
conn->le.keys->ltk.ediv,
|
|
conn->le.keys->ltk.val,
|
|
conn->le.keys->enc_size);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
atomic_set_bit(smp->allowed_cmds, BT_SMP_CMD_SECURITY_REQUEST);
|
|
atomic_set_bit(smp->flags, SMP_FLAG_ENC_PENDING);
|
|
return 0;
|
|
}
|
|
#endif /* CONFIG_BT_CENTRAL && CONFIG_BT_SMP */
|
|
#if defined(CONFIG_BT_PERIPHERAL)
|
|
case BT_HCI_ROLE_PERIPHERAL:
|
|
return smp_send_security_req(conn);
|
|
#endif /* CONFIG_BT_PERIPHERAL && CONFIG_BT_SMP */
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
void bt_smp_update_keys(struct bt_conn *conn)
|
|
{
|
|
struct bt_smp *smp;
|
|
|
|
smp = smp_chan_get(conn);
|
|
if (!smp) {
|
|
return;
|
|
}
|
|
|
|
if (!atomic_test_bit(smp->flags, SMP_FLAG_PAIRING)) {
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* If link was successfully encrypted cleanup old keys as from now on
|
|
* only keys distributed in this pairing or LTK from LE SC will be used.
|
|
*/
|
|
if (conn->le.keys) {
|
|
bt_keys_clear(conn->le.keys);
|
|
}
|
|
|
|
conn->le.keys = bt_keys_get_addr(conn->id, &conn->le.dst);
|
|
if (!conn->le.keys) {
|
|
LOG_ERR("Unable to get keys for %s", bt_addr_le_str(&conn->le.dst));
|
|
smp_error(smp, BT_SMP_ERR_UNSPECIFIED);
|
|
return;
|
|
}
|
|
|
|
/* mark keys as debug */
|
|
if (atomic_test_bit(smp->flags, SMP_FLAG_SC_DEBUG_KEY)) {
|
|
conn->le.keys->flags |= BT_KEYS_DEBUG;
|
|
}
|
|
|
|
/*
|
|
* store key type deducted from pairing method used
|
|
* it is important to store it since type is used to determine
|
|
* security level upon encryption
|
|
*/
|
|
switch (smp->method) {
|
|
case LE_SC_OOB:
|
|
case LEGACY_OOB:
|
|
conn->le.keys->flags |= BT_KEYS_OOB;
|
|
/* fallthrough */
|
|
case PASSKEY_DISPLAY:
|
|
case PASSKEY_INPUT:
|
|
case PASSKEY_CONFIRM:
|
|
conn->le.keys->flags |= BT_KEYS_AUTHENTICATED;
|
|
break;
|
|
case JUST_WORKS:
|
|
default:
|
|
/* unauthenticated key, clear it */
|
|
conn->le.keys->flags &= ~BT_KEYS_OOB;
|
|
conn->le.keys->flags &= ~BT_KEYS_AUTHENTICATED;
|
|
break;
|
|
}
|
|
|
|
conn->le.keys->enc_size = get_encryption_key_size(smp);
|
|
|
|
/*
|
|
* Store LTK if LE SC is used, this is safe since LE SC is mutually
|
|
* exclusive with legacy pairing. Other keys are added on keys
|
|
* distribution.
|
|
*/
|
|
if (atomic_test_bit(smp->flags, SMP_FLAG_SC)) {
|
|
conn->le.keys->flags |= BT_KEYS_SC;
|
|
|
|
if (atomic_test_bit(smp->flags, SMP_FLAG_BOND)) {
|
|
bt_keys_add_type(conn->le.keys, BT_KEYS_LTK_P256);
|
|
memcpy(conn->le.keys->ltk.val, smp->tk,
|
|
sizeof(conn->le.keys->ltk.val));
|
|
(void)memset(conn->le.keys->ltk.rand, 0,
|
|
sizeof(conn->le.keys->ltk.rand));
|
|
(void)memset(conn->le.keys->ltk.ediv, 0,
|
|
sizeof(conn->le.keys->ltk.ediv));
|
|
} else if (IS_ENABLED(CONFIG_BT_LOG_SNIFFER_INFO)) {
|
|
uint8_t ltk[16];
|
|
|
|
sys_memcpy_swap(ltk, smp->tk, conn->le.keys->enc_size);
|
|
LOG_INF("SC LTK: 0x%s (No bonding)", bt_hex(ltk, conn->le.keys->enc_size));
|
|
}
|
|
} else {
|
|
conn->le.keys->flags &= ~BT_KEYS_SC;
|
|
}
|
|
}
|
|
|
|
static int bt_smp_accept(struct bt_conn *conn, struct bt_l2cap_chan **chan)
|
|
{
|
|
int i;
|
|
static const struct bt_l2cap_chan_ops ops = {
|
|
.connected = bt_smp_connected,
|
|
.disconnected = bt_smp_disconnected,
|
|
.encrypt_change = bt_smp_encrypt_change,
|
|
.recv = bt_smp_recv,
|
|
};
|
|
|
|
LOG_DBG("conn %p handle %u", conn, conn->handle);
|
|
|
|
for (i = 0; i < ARRAY_SIZE(bt_smp_pool); i++) {
|
|
struct bt_smp *smp = &bt_smp_pool[i];
|
|
|
|
if (smp->chan.chan.conn) {
|
|
continue;
|
|
}
|
|
|
|
smp->chan.chan.ops = &ops;
|
|
|
|
*chan = &smp->chan.chan;
|
|
|
|
return 0;
|
|
}
|
|
|
|
LOG_ERR("No available SMP context for conn %p", conn);
|
|
|
|
return -ENOMEM;
|
|
}
|
|
|
|
BT_L2CAP_CHANNEL_DEFINE(smp_fixed_chan, BT_L2CAP_CID_SMP, bt_smp_accept, NULL);
|
|
#if defined(CONFIG_BT_BREDR)
|
|
BT_L2CAP_CHANNEL_DEFINE(smp_br_fixed_chan, BT_L2CAP_CID_BR_SMP,
|
|
bt_smp_br_accept, NULL);
|
|
#endif /* CONFIG_BT_BREDR */
|
|
|
|
int bt_smp_init(void)
|
|
{
|
|
static struct bt_pub_key_cb pub_key_cb = {
|
|
.func = bt_smp_pkey_ready,
|
|
};
|
|
|
|
sc_supported = le_sc_supported();
|
|
if (IS_ENABLED(CONFIG_BT_SMP_SC_PAIR_ONLY) && !sc_supported) {
|
|
LOG_ERR("SC Pair Only Mode selected but LE SC not supported");
|
|
return -ENOENT;
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_SMP_USB_HCI_CTLR_WORKAROUND)) {
|
|
LOG_WRN("BT_SMP_USB_HCI_CTLR_WORKAROUND is enabled, which "
|
|
"exposes a security vulnerability!");
|
|
}
|
|
|
|
LOG_DBG("LE SC %s", sc_supported ? "enabled" : "disabled");
|
|
|
|
if (!IS_ENABLED(CONFIG_BT_SMP_OOB_LEGACY_PAIR_ONLY)) {
|
|
bt_pub_key_gen(&pub_key_cb);
|
|
}
|
|
|
|
return smp_self_test();
|
|
}
|