Bluetooth SMP: added support for Legacy pairing (MITM) with passkey

#   1: .codespellrc
  #  /home/runner/work/nuttx/nuttx/nuttx/include/nuttx/uorb.h:307: afte ==> after
   # /home/runner/work/nuttx/nuttx/nuttx/include/nuttx/uorb.h:405: multipled ==> multiplied
    #/home/runner/work/nuttx/nuttx/nuttx/include/nuttx/uorb.h:416: multipled ==> multiplied
    #/home/runner/work/nuttx/nuttx/nuttx/include/nuttx/uorb.h:432: provies ==> provides, proves
    #/home/runner/work/nuttx/nuttx/nuttx/include/nuttx/uorb.h:1173: subcribers ==> subscribers
    #Error: Process completed with exit code 1.
This commit is contained in:
robert
2025-05-11 08:54:59 -04:00
committed by Xiang Xiao
parent ab756e8dd7
commit df058b462e
7 changed files with 528 additions and 101 deletions

View File

@@ -174,6 +174,22 @@ config BLUETOOTH_CNTRL_HOST_FLOW_DISABLE
indicate to the Host when its buffers are nearly full, allowing the Host to
stop sending data until buffer space becomes available.
config BLUETOOTH_SMP_IO_CAPABILITY
int "Bluetooth SMP I/O Capability"
default 3
range 0 4
---help---
Defines the Input/Output capabilities of this device
for SMP pairing purposes.
Values based on Bluetooth Core Spec v4.2, Vol 3, Part H, Section 2.3.2:
0: DisplayOnly (Can display a 6-digit code)
1: DisplayYesNo (Can display a 6-digit code and has Yes/No input)
2: KeyboardOnly (Has numeric keyboard input, no display)
3: NoInputNoOutput (Cannot display or input codes, e.g., headset)
4: KeyboardDisplay (Has both display and keyboard)
NOTE: Current implementation supports NoInputNoOutput and DisplayOnly.
menu "Kernel Thread Configuration"
config BLUETOOTH_TXCMD_STACKSIZE

View File

@@ -1037,22 +1037,31 @@ static uint8_t check_perm(FAR struct bt_conn_s *conn,
FAR const struct bt_gatt_attr_s *attr,
uint8_t mask)
{
if ((mask & BT_GATT_PERM_READ) && !(attr->perm & BT_GATT_PERM_READ))
if ((mask & BT_GATT_PERM_READ) &&
(!(attr->perm & BT_GATT_PERM_READ_MASK) || !attr->read))
{
return BT_ATT_ERR_READ_NOT_PERMITTED;
}
if ((mask & BT_GATT_PERM_WRITE) && !(attr->perm & BT_GATT_PERM_WRITE))
if ((mask & BT_GATT_PERM_WRITE) &&
(!(attr->perm & BT_GATT_PERM_WRITE_MASK) || !attr->write))
{
return BT_ATT_ERR_READ_NOT_PERMITTED;
return BT_ATT_ERR_WRITE_NOT_PERMITTED;
}
mask &= attr->perm;
if (mask & BT_GATT_PERM_AUTHEN_MASK)
{
/* TODO: Check conn authentication */
if (!conn->encrypt || conn->sec_level < BT_SECURITY_HIGH)
{
wlerr("Auth Fail - encrypt=%d, level=%d (need >= %d)\n",
conn->encrypt, conn->sec_level, BT_SECURITY_HIGH);
return BT_ATT_ERR_AUTHENTICATION;
}
return BT_ATT_ERR_AUTHENTICATION;
wlinfo("Auth OK - encrypt=%d, level=%d\n",
conn->encrypt, conn->sec_level);
mask &= ~BT_GATT_PERM_AUTHEN_MASK;
}
if ((mask & BT_GATT_PERM_ENCRYPT_MASK) && !conn->encrypt)

View File

@@ -853,6 +853,10 @@ int bt_conn_security(FAR struct bt_conn_s *conn, enum bt_security_e sec)
return -ENOTCONN;
}
/* Store the requested security level */
conn->sec_level = sec;
/* Nothing to do */
if (sec == BT_SECURITY_LOW)
@@ -860,9 +864,9 @@ int bt_conn_security(FAR struct bt_conn_s *conn, enum bt_security_e sec)
return 0;
}
/* For now we only support JustWorks */
/* For now we only support Just Works and MITM with passkey (Legacy only) */
if (sec > BT_SECURITY_MEDIUM)
if (sec > BT_SECURITY_HIGH)
{
return -EINVAL;
}

View File

@@ -102,6 +102,7 @@ struct bt_conn_s
FAR void *smp;
uint8_t le_conn_interval;
enum bt_security_e sec_level;
bt_atomic_t ref;
enum bt_conn_state_e state;

View File

@@ -62,6 +62,7 @@ struct bt_ltk_s
uint64_t rand;
uint16_t ediv;
uint8_t val[16];
enum bt_security_e level;
FAR struct bt_keys_s *next;
};

View File

@@ -71,6 +71,16 @@
/* SMP channel specific context */
enum smp_pairing_method_e
{
PAIRING_METHOD_JUST_WORKS,
PAIRING_METHOD_PASSKEY_DISPLAY, /* Local displays, remote inputs */
PAIRING_METHOD_PASSKEY_INPUT, /* Local inputs, remote displays */
PAIRING_METHOD_OOB,
PAIRING_METHOD_NUM_COMP, /* LESC only, not implemented yet */
PAIRING_METHOD_NOT_SUPPORTED
};
struct bt_smp_s
{
/* The connection this context is associated with */
@@ -85,6 +95,14 @@ struct bt_smp_s
bool pending_encrypt;
/* Selected pairing method */
enum smp_pairing_method_e selected_method;
/* Passkey */
uint32_t passkey;
/* Pairing Request PDU */
uint8_t preq[7];
@@ -145,7 +163,7 @@ static int le_rand(FAR void *buf, size_t len);
static int smp_ah(FAR const uint8_t irk[16], FAR const uint8_t r[3],
FAR uint8_t out[3]);
static int smp_c1(FAR const uint8_t k[16], FAR const uint8_t r[16],
FAR const uint8_t preq[7], FAR const uint8_t pres[7],
FAR const uint8_t preq[7], FAR const uint8_t presp[7],
FAR const bt_addr_le_t *ia, FAR const bt_addr_le_t *ra,
FAR uint8_t enc_data[16]);
static int smp_s1(FAR const uint8_t k[16], FAR const uint8_t r1[16],
@@ -176,6 +194,13 @@ static uint8_t smp_ident_addr_info(FAR struct bt_conn_s *conn,
FAR struct bt_buf_s *buf);
static uint8_t smp_security_request(FAR struct bt_conn_s *conn,
FAR struct bt_buf_s *buf);
static void smp_auth_pairing_cancel(FAR struct bt_conn_s *conn);
static void smp_auth_passkey_display(FAR struct bt_conn_s *conn,
unsigned int passkey);
static void smp_auth_pairing_complete(FAR struct bt_conn_s *conn,
bool bonded);
static void smp_auth_pairing_failed(FAR struct bt_conn_s *conn,
uint8_t reason);
static void bt_smp_receive(FAR struct bt_conn_s *conn,
FAR struct bt_buf_s *buf, FAR void *context,
uint16_t cid);
@@ -209,6 +234,14 @@ static int smp_self_test(void);
****************************************************************************/
static struct bt_smp_s g_smp_pool[CONFIG_BLUETOOTH_MAX_CONN];
static const struct bt_smp_auth_cb_s g_smp_auth_default_cb =
{
smp_auth_passkey_display,
smp_auth_pairing_cancel,
smp_auth_pairing_complete,
smp_auth_pairing_failed
};
static const struct bt_smp_auth_cb_s *g_smp_auth_cb = &g_smp_auth_default_cb;
static const struct bt_smphandlers_s g_smp_handlers[] =
{
{
@@ -458,7 +491,7 @@ static int smp_ah(FAR const uint8_t irk[16], FAR const uint8_t r[3],
}
static int smp_c1(FAR const uint8_t k[16], FAR const uint8_t r[16],
FAR const uint8_t preq[7], FAR const uint8_t pres[7],
FAR const uint8_t preq[7], FAR const uint8_t presp[7],
FAR const bt_addr_le_t *ia, FAR const bt_addr_le_t *ra,
FAR uint8_t enc_data[16])
{
@@ -468,14 +501,14 @@ static int smp_c1(FAR const uint8_t k[16], FAR const uint8_t r[16],
wlinfo("k %s r %s\n", h(k, 16), h(r, 16));
wlinfo("ia %s ra %s\n", bt_addr_le_str(ia), bt_addr_le_str(ra));
wlinfo("preq %s pres %s\n", h(preq, 7), h(pres, 7));
wlinfo("preq %s presp %s\n", h(preq, 7), h(presp, 7));
/* pres, preq, rat and iat are concatenated to generate p1 */
/* presp, 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);
memcpy(p1 + 9, presp, 7);
wlinfo("p1 %s\n", h(p1, 16));
@@ -556,6 +589,34 @@ static void send_err_rsp(FAR struct bt_conn_s *conn, uint8_t reason)
rsp->reason = reason;
bt_l2cap_send(conn, BT_L2CAP_CID_SMP, buf);
if (g_smp_auth_cb && g_smp_auth_cb->pairing_failed)
{
g_smp_auth_cb->pairing_failed(conn, reason);
}
}
static void smp_auth_pairing_cancel(FAR struct bt_conn_s *conn)
{
wlwarn("Pairing cancelled (conn=%p)\n", conn);
}
static void smp_auth_passkey_display(FAR struct bt_conn_s *conn,
unsigned int passkey)
{
wlwarn("Passkey: %d", passkey);
}
static void smp_auth_pairing_complete(FAR struct bt_conn_s *conn,
bool bonded)
{
wlwarn("Bonding status: %s", bonded ? "success" : "failed");
}
static void smp_auth_pairing_failed(FAR struct bt_conn_s *conn,
uint8_t reason)
{
wlwarn("Pairing failed with reason code %d", reason);
}
static int smp_init(struct bt_smp_s *smp)
@@ -576,6 +637,62 @@ static int smp_init(struct bt_smp_s *smp)
return 0;
}
static enum smp_pairing_method_e smp_get_pairing_method(uint8_t local_io,
uint8_t remote_io,
uint8_t local_auth,
uint8_t remote_auth)
{
bool local_mitm = (local_auth & BT_SMP_AUTH_MITM);
bool remote_mitm = (remote_auth & BT_SMP_AUTH_MITM);
bool mitm_requested = local_mitm || remote_mitm;
wlinfo("Local IO: %d, Remote IO: %d, MITM Req: %d\n", local_io, remote_io,
mitm_requested);
/* Mapping based on Core Spec v4.2, Vol 3, Part H, Table 2.8
* and Figure 2.7 flow
*/
switch (local_io)
{
case BT_SMP_IO_DISPLAY_ONLY:
switch (remote_io)
{
case BT_SMP_IO_DISPLAY_ONLY:
case BT_SMP_IO_NO_INPUT_OUTPUT:
return PAIRING_METHOD_JUST_WORKS;
case BT_SMP_IO_KEYBOARD_ONLY:
case BT_SMP_IO_KEYBOARD_DISPLAY:
case BT_SMP_IO_DISPLAY_YESNO:
return mitm_requested ? PAIRING_METHOD_PASSKEY_DISPLAY
: PAIRING_METHOD_JUST_WORKS;
default:
return PAIRING_METHOD_NOT_SUPPORTED;
}
break;
case BT_SMP_IO_NO_INPUT_OUTPUT:
return PAIRING_METHOD_JUST_WORKS; /* Cannot support MITM */
default:
wlwarn("Unhandled local IO Cap %d\n", local_io);
return PAIRING_METHOD_JUST_WORKS;
}
}
static bool smp_mitm_supported(enum smp_pairing_method_e method)
{
return method == PAIRING_METHOD_PASSKEY_DISPLAY ||
method == PAIRING_METHOD_PASSKEY_INPUT;
}
static void smp_passkey_to_tk(uint32_t passkey, uint8_t *tk)
{
memset(tk, 0, 16);
tk[0] = passkey & 0xff;
tk[1] = (passkey >> 8) & 0xff;
tk[2] = (passkey >> 16) & 0xff;
tk[3] = (passkey >> 24) & 0xff;
}
static uint8_t smp_pairing_req(FAR struct bt_conn_s *conn,
FAR struct bt_buf_s *buf)
{
@@ -583,10 +700,16 @@ static uint8_t smp_pairing_req(FAR struct bt_conn_s *conn,
FAR struct bt_smp_pairing_s *rsp;
FAR struct bt_buf_s *rsp_buf;
FAR struct bt_smp_s *smp = conn->smp;
uint8_t auth;
uint8_t local_io_cap = CONFIG_BLUETOOTH_SMP_IO_CAPABILITY;
uint8_t local_auth_req = BT_SMP_AUTH_BONDING;
int ret;
wlinfo("\n");
if (CONFIG_BLUETOOTH_SMP_IO_CAPABILITY != BT_SMP_IO_NO_INPUT_OUTPUT)
{
local_auth_req |= BT_SMP_AUTH_MITM;
}
wlinfo("Pairing Request Received\n");
if ((req->max_key_size > BT_SMP_MAX_ENC_KEY_SIZE) ||
(req->max_key_size < BT_SMP_MIN_ENC_KEY_SIZE))
@@ -600,6 +723,55 @@ static uint8_t smp_pairing_req(FAR struct bt_conn_s *conn,
return ret;
}
/* Perform pairing method selection before sending response */
smp->selected_method = smp_get_pairing_method(local_io_cap,
req->io_capability,
local_auth_req,
req->auth_req);
wlinfo("Selected pairing method: %d\n", smp->selected_method);
if (conn->sec_level >= BT_SECURITY_HIGH &&
!smp_mitm_supported(smp->selected_method))
{
wlerr("ERROR: Cannot achieve HIGH security (MITM) with "
"selected method %d\n", smp->selected_method);
return BT_SMP_ERR_AUTH_REQUIREMENTS;
}
if (smp->selected_method == PAIRING_METHOD_NOT_SUPPORTED)
{
wlerr("ERROR: Pairing method for IO Caps %d/%d not supported\n",
local_io_cap, req->io_capability);
return BT_SMP_ERR_PAIRING_NOTSUPP;
}
if (smp->selected_method == PAIRING_METHOD_PASSKEY_DISPLAY)
{
uint32_t passkey;
le_rand(&passkey, sizeof(passkey));
passkey %= 1000000; /* 6 digit passkey */
smp->passkey = passkey;
wlwarn("Using Passkey Display method. Generated Passkey: %06d\n",
(unsigned int) passkey);
smp_passkey_to_tk(passkey, smp->tk);
if (g_smp_auth_cb && g_smp_auth_cb->passkey_display)
{
g_smp_auth_cb->passkey_display(conn, passkey);
}
}
else if (smp->selected_method == PAIRING_METHOD_JUST_WORKS)
{
wlinfo("Using Just Works method.\n");
memset(smp->tk, 0, sizeof(smp->tk));
}
else
{
wlerr("ERROR: Invalid selected method %d here\n",
smp->selected_method);
return BT_SMP_ERR_UNSPECIFIED;
}
rsp_buf = bt_smp_create_pdu(conn, BT_SMP_CMD_PAIRING_RSP, sizeof(*rsp));
if (!rsp_buf)
{
@@ -608,25 +780,17 @@ static uint8_t smp_pairing_req(FAR struct bt_conn_s *conn,
rsp = bt_buf_extend(rsp_buf, sizeof(*rsp));
/* For JustWorks pairing simplify rsp parameters.
* TODO: needs to be reworked later on.
*/
auth = (req->auth_req & BT_SMP_AUTH_MASK);
auth &= ~(BT_SMP_AUTH_MITM | BT_SMP_AUTH_SC |
BT_SMP_AUTH_KEYPRESS);
rsp->auth_req = auth;
rsp->io_capability = BT_SMP_IO_NO_INPUT_OUTPUT;
rsp->io_capability = local_io_cap;
rsp->auth_req = local_auth_req;
rsp->oob_flag = BT_SMP_OOB_NOT_PRESENT;
rsp->max_key_size = req->max_key_size;
rsp->max_key_size = MIN(req->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);
smp->local_dist = rsp->resp_key_dist;
smp->remote_dist = rsp->init_key_dist;
memset(smp->tk, 0, sizeof(smp->tk));
/* Store req/rsp for later use */
smp->preq[0] = BT_SMP_CMD_PAIRING_REQ;
@@ -687,8 +851,10 @@ static uint8_t smp_pairing_rsp(FAR struct bt_conn_s *conn,
{
struct bt_smp_pairing_s *rsp = (FAR void *)buf->data;
struct bt_smp_s *smp = conn->smp;
uint8_t local_io_cap = CONFIG_BLUETOOTH_SMP_IO_CAPABILITY;
uint8_t local_auth_req = smp->preq[3];
wlinfo("\n");
wlinfo("Pairing Response Received\n");
if ((rsp->max_key_size > BT_SMP_MAX_ENC_KEY_SIZE) ||
(rsp->max_key_size < BT_SMP_MIN_ENC_KEY_SIZE))
@@ -696,11 +862,55 @@ static uint8_t smp_pairing_rsp(FAR struct bt_conn_s *conn,
return BT_SMP_ERR_ENC_KEY_SIZE;
}
smp->selected_method = smp_get_pairing_method(local_io_cap,
rsp->io_capability,
local_auth_req,
rsp->auth_req);
wlinfo("Selected pairing method: %d\n", smp->selected_method);
if (conn->sec_level >= BT_SECURITY_HIGH &&
!smp_mitm_supported(smp->selected_method))
{
wlerr("ERROR: Cannot achieve HIGH security (MITM) with selected "
"method %d\n", smp->selected_method);
return BT_SMP_ERR_AUTH_REQUIREMENTS;
}
if (smp->selected_method == PAIRING_METHOD_NOT_SUPPORTED)
{
wlerr("ERROR: Pairing method for IO Caps %d/%d not supported\n",
local_io_cap, rsp->io_capability);
return BT_SMP_ERR_PAIRING_NOTSUPP;
}
if (smp->selected_method == PAIRING_METHOD_PASSKEY_DISPLAY)
{
uint32_t passkey;
le_rand(&passkey, sizeof(passkey));
passkey %= 1000000; /* 6 digit passkey */
smp->passkey = passkey;
wlinfo("Using Passkey Display method. Generated Passkey: %06u\n",
(unsigned int) passkey);
smp_passkey_to_tk(passkey, smp->tk);
if (g_smp_auth_cb && g_smp_auth_cb->passkey_display)
{
g_smp_auth_cb->passkey_display(conn, passkey);
}
}
else if (smp->selected_method == PAIRING_METHOD_JUST_WORKS)
{
wlwarn("Using Just Works method.\n");
memset(smp->tk, 0, sizeof(smp->tk));
}
else
{
wlerr("ERROR: Invalid selected method %d\n", smp->selected_method);
return BT_SMP_ERR_UNSPECIFIED;
}
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));
@@ -758,9 +968,10 @@ static uint8_t smp_pairing_random(FAR struct bt_conn_s *conn,
FAR struct bt_smp_s *smp = conn->smp;
FAR struct bt_keys_s *keys;
uint8_t cfm[16];
enum bt_security_e pairing_sec_level;
int err;
wlinfo("\n");
wlinfo("Received Pairing Random\n");
memcpy(smp->rrnd, req->val, sizeof(smp->rrnd));
@@ -785,60 +996,98 @@ static uint8_t smp_pairing_random(FAR struct bt_conn_s *conn,
if (memcmp(smp->pcnf, cfm, sizeof(smp->pcnf)))
{
wlerr("ERROR: Pairing Confirm verification failed!\n");
return BT_SMP_ERR_CONFIRM_FAILED;
}
wlinfo("Pairing Confirm verified successfully.\n");
/* Determine security level achieved by pairing method */
switch (smp->selected_method)
{
case PAIRING_METHOD_PASSKEY_DISPLAY:
case PAIRING_METHOD_PASSKEY_INPUT:
pairing_sec_level = BT_SECURITY_HIGH;
break;
case PAIRING_METHOD_JUST_WORKS:
default:
pairing_sec_level = BT_SECURITY_MEDIUM;
break;
}
wlinfo("Pairing method %d achieved security level %d\n",
smp->selected_method, pairing_sec_level);
conn->sec_level = pairing_sec_level;
/* Get/Create the keys structure for the peer */
keys = bt_keys_get_addr(&conn->dst);
if (!keys)
{
wlerr("ERROR: Failed to get/create keys entry for %s\n",
bt_addr_le_str(&conn->dst));
return BT_SMP_ERR_UNSPECIFIED;
}
if (conn->role == BT_HCI_ROLE_MASTER)
{
uint8_t stk[16];
/* No need to store master STK */
err = smp_s1(smp->tk, smp->rrnd, smp->prnd, stk);
if (err)
{
return BT_SMP_ERR_UNSPECIFIED;
}
/* Rand and EDiv are 0 for the STK */
wlinfo("Master generated STK: %s\n", h(stk, 16));
bt_keys_add_type(keys, BT_KEYS_LTK);
keys->ltk.level = pairing_sec_level;
/* Start encryption using the generated STK */
if (bt_conn_le_start_encryption(conn, 0, 0, stk))
{
wlerr("ERROR: Failed to start encryption\n");
wlerr("ERROR: Failed to start encryption with STK\n");
bt_keys_clear(keys, BT_KEYS_LTK);
return BT_SMP_ERR_UNSPECIFIED;
}
smp->pending_encrypt = true;
bt_atomic_set(&smp->allowed_cmds, 0);
bt_atomic_setbit(&smp->allowed_cmds, BT_SMP_CMD_PAIRING_FAIL);
return 0;
}
keys = bt_keys_get_type(BT_KEYS_SLAVE_LTK, &conn->dst);
if (keys == NULL)
else
{
wlerr("ERROR: Unable to create new keys\n");
return BT_SMP_ERR_UNSPECIFIED;
/* Slave Role: Generate and store STK as the Slave LTK */
bt_keys_add_type(keys, BT_KEYS_SLAVE_LTK);
err = smp_s1(smp->tk, smp->prnd, smp->rrnd, keys->slave_ltk.val);
if (err)
{
bt_keys_clear(keys, BT_KEYS_SLAVE_LTK);
return BT_SMP_ERR_UNSPECIFIED;
}
keys->slave_ltk.level = pairing_sec_level;
/* Rand and EDiv are 0 for the STK */
keys->slave_ltk.rand = 0;
keys->slave_ltk.ediv = 0;
wlinfo("Slave generated STK/SlaveLTK: %s (level %d)\n",
h(keys->slave_ltk.val, 16), keys->slave_ltk.level);
smp->pending_encrypt = true;
smp_send_pairing_random(conn);
bt_atomic_set(&smp->allowed_cmds, 0);
bt_atomic_setbit(&smp->allowed_cmds, BT_SMP_CMD_PAIRING_FAIL);
return 0;
}
err = smp_s1(smp->tk, smp->prnd, smp->rrnd, keys->slave_ltk.val);
if (err)
{
bt_keys_clear(keys, BT_KEYS_SLAVE_LTK);
return BT_SMP_ERR_UNSPECIFIED;
}
/* Rand and EDiv are 0 for the STK */
keys->slave_ltk.rand = 0;
keys->slave_ltk.ediv = 0;
wlinfo("generated STK %s\n", h(keys->slave_ltk.val, 16));
smp->pending_encrypt = true;
smp_send_pairing_random(conn);
return 0;
}
static uint8_t smp_pairing_failed(FAR struct bt_conn_s *conn,
@@ -850,6 +1099,11 @@ static uint8_t smp_pairing_failed(FAR struct bt_conn_s *conn,
wlerr("ERROR: reason 0x%x\n", req->reason);
UNUSED(req);
if (g_smp_auth_cb && g_smp_auth_cb->pairing_failed)
{
g_smp_auth_cb->pairing_failed(conn, req->reason);
}
bt_atomic_set(&smp->allowed_cmds, 0);
bt_atomic_setbit(&smp->allowed_cmds, BT_SMP_CMD_PAIRING_FAIL);
@@ -867,6 +1121,53 @@ static uint8_t smp_pairing_failed(FAR struct bt_conn_s *conn,
return 0;
}
static bool smp_check_pairing_complete(FAR struct bt_conn_s *conn)
{
FAR struct bt_smp_s *smp = conn->smp;
bool complete = false;
bool bonded = false;
if (!smp) return false;
/* Pairing is considered complete when all keys BOTH sides intended to
* distribute have been successfully sent/received.
*/
if (smp->local_dist == 0 && smp->remote_dist == 0)
{
complete = true;
uint8_t local_auth = (conn->role == BT_HCI_ROLE_MASTER) ? smp->preq[3]
: smp->prsp[3];
uint8_t remote_auth = (conn->role == BT_HCI_ROLE_MASTER) ? smp->prsp[3]
: smp->preq[3];
if ((local_auth & BT_SMP_AUTH_BONDING) &&
(remote_auth & BT_SMP_AUTH_BONDING))
{
bonded = true;
}
wlinfo("Pairing complete. Bonded: %d\n", bonded);
if (g_smp_auth_cb && g_smp_auth_cb->pairing_complete)
{
g_smp_auth_cb->pairing_complete(conn, bonded);
}
bt_atomic_set(&smp->allowed_cmds, 0);
bt_atomic_setbit(&smp->allowed_cmds, BT_SMP_CMD_PAIRING_FAIL);
if (conn->role == BT_HCI_ROLE_MASTER)
{
bt_atomic_setbit(&smp->allowed_cmds, BT_SMP_CMD_SECURITY_REQUEST);
}
else
{
bt_atomic_setbit(&smp->allowed_cmds, BT_SMP_CMD_PAIRING_REQ);
}
}
return complete;
}
static void bt_smp_distribute_keys(FAR struct bt_conn_s *conn)
{
FAR struct bt_smp_s *smp = conn->smp;
@@ -924,6 +1225,13 @@ static void bt_smp_distribute_keys(FAR struct bt_conn_s *conn)
ident->ediv = keys->slave_ltk.ediv;
bt_l2cap_send(conn, BT_L2CAP_CID_SMP, buf);
smp->local_dist &= ~BT_SMP_DIST_ENC_KEY;
}
if (smp->local_dist == 0)
{
wlinfo("Finished distributing local keys.\n");
smp_check_pairing_complete(conn);
}
}
@@ -934,7 +1242,7 @@ static uint8_t smp_encrypt_info(FAR struct bt_conn_s *conn,
FAR struct bt_smp_s *smp = conn->smp;
FAR struct bt_keys_s *keys;
wlinfo("\n");
wlinfo("Received Encrypt Info (LTK from Master)\n");
keys = bt_keys_get_type(BT_KEYS_LTK, &conn->dst);
if (!keys)
@@ -945,6 +1253,7 @@ static uint8_t smp_encrypt_info(FAR struct bt_conn_s *conn,
}
memcpy(keys->ltk.val, req->ltk, 16);
keys->ltk.level = conn->sec_level;
bt_atomic_setbit(&smp->allowed_cmds, BT_SMP_CMD_MASTER_IDENT);
@@ -958,7 +1267,7 @@ static uint8_t smp_master_ident(FAR struct bt_conn_s *conn,
FAR struct bt_smp_s *smp = conn->smp;
FAR struct bt_keys_s *keys;
wlinfo("\n");
wlinfo("Received Master Identification (EDIV/Rand)\n");
keys = bt_keys_get_type(BT_KEYS_LTK, &conn->dst);
if (!keys)
@@ -968,17 +1277,34 @@ static uint8_t smp_master_ident(FAR struct bt_conn_s *conn,
return BT_SMP_ERR_UNSPECIFIED;
}
if (!(keys->keys & BT_KEYS_LTK))
{
return BT_SMP_ERR_UNSPECIFIED;
}
keys->ltk.ediv = req->ediv;
keys->ltk.rand = req->rand;
smp->remote_dist &= ~BT_SMP_DIST_ENC_KEY;
if (conn->role == BT_HCI_ROLE_MASTER)
if (conn->role == BT_HCI_ROLE_SLAVE)
{
smp->remote_dist &= ~BT_SMP_DIST_ENC_KEY;
if (!smp->remote_dist)
if (smp->local_dist && !smp->remote_dist)
{
wlinfo("Slave distributing keys now.\n");
bt_smp_distribute_keys(conn);
}
}
else
{
/* conn->role == BT_HCI_ROLE_MASTER */
return 0;
if (!smp->local_dist && !smp->remote_dist)
{
wlinfo("Master: Key distribution complete.\n");
if (g_smp_auth_cb && g_smp_auth_cb->pairing_complete)
{
g_smp_auth_cb->pairing_complete(conn, true);
}
}
}
@@ -1026,7 +1352,7 @@ static uint8_t smp_ident_addr_info(FAR struct bt_conn_s *conn,
if (!bt_addr_le_is_identity(&req->addr))
{
wlerr("ERROR: Invalid identity %s for %s\n",
bt_addr_le_str(&req->addr), bt_addr_le_str(&conn->dst));
bt_addr_le_str(&req->addr), bt_addr_le_str(&conn->dst));
return BT_SMP_ERR_INVALID_PARAMS;
}
@@ -1045,14 +1371,27 @@ static uint8_t smp_ident_addr_info(FAR struct bt_conn_s *conn,
bt_addr_le_copy(&conn->dst, &req->addr);
}
if (conn->role == BT_HCI_ROLE_MASTER)
smp->remote_dist &= ~BT_SMP_DIST_ID_KEY;
if (conn->role == BT_HCI_ROLE_SLAVE)
{
smp->remote_dist &= ~BT_SMP_DIST_ID_KEY;
if (!smp->remote_dist)
if (smp->local_dist && !smp->remote_dist)
{
wlinfo("Slave distributing keys now.\n");
bt_smp_distribute_keys(conn);
}
}
else
{
if (!smp->local_dist && !smp->remote_dist)
{
wlinfo("Master: Key distribution complete.\n");
if (g_smp_auth_cb && g_smp_auth_cb->pairing_complete)
{
g_smp_auth_cb->pairing_complete(conn, true);
}
}
}
return 0;
}
@@ -1062,30 +1401,49 @@ static uint8_t smp_security_request(FAR struct bt_conn_s *conn,
{
FAR struct bt_smp_security_request_s *req = (FAR void *)buf->data;
FAR struct bt_keys_s *keys;
uint8_t auth;
uint8_t slave_auth_req = req->auth_req & BT_SMP_AUTH_MASK;
wlinfo("\n");
wlinfo("Security Request received (req=0x%02x)\n", slave_auth_req);
keys = bt_keys_find(BT_KEYS_LTK, &conn->dst);
if (!keys)
if (keys)
{
bool mitm_required_by_slave = (slave_auth_req & BT_SMP_AUTH_MITM);
bool key_has_mitm = (keys->ltk.level >= BT_SECURITY_HIGH);
wlinfo("Found existing LTK (level=%d)\n", keys->ltk.level);
if (mitm_required_by_slave && !key_has_mitm)
{
/* Slave requires MITM, but our key doesn't have it. Re-pair. */
wlinfo("Existing key level %d insufficient for slave MITM req. "
"Re-pairing.\n", keys->ltk.level);
goto pair;
}
else
{
wlinfo("Attempting encryption with existing key.\n");
if (bt_conn_le_start_encryption(conn, keys->ltk.rand,
keys->ltk.ediv, keys->ltk.val) == 0)
{
wlinfo("Encryption started successfully.\n");
conn->sec_level = keys->ltk.level;
return 0;
}
else
{
wlerr("ERROR: Failed to start encryption with existing keys. "
"Pairing.\n");
goto pair;
}
}
}
else
{
wlinfo("No existing keys found.\n");
goto pair;
}
auth = req->auth_req & BT_SMP_AUTH_MASK;
if (auth & (BT_SMP_AUTH_MITM | BT_SMP_AUTH_SC))
{
wlwarn("Unsupported auth requirements: 0x%x, repairing", auth);
goto pair;
}
if (bt_conn_le_start_encryption(conn, keys->ltk.rand, keys->ltk.ediv,
keys->ltk.val) < 0)
{
return BT_SMP_ERR_UNSPECIFIED;
}
return 0;
pair:
if (bt_smp_send_pairing_req(conn) < 0)
{
@@ -1198,6 +1556,8 @@ static void bt_smp_disconnected(FAR struct bt_conn_s *conn,
FAR void *context, uint16_t cid)
{
FAR struct bt_smp_s *smp = conn->smp;
bool pairing_active =
(smp && smp->selected_method != PAIRING_METHOD_JUST_WORKS);
if (!smp)
{
@@ -1208,6 +1568,11 @@ static void bt_smp_disconnected(FAR struct bt_conn_s *conn,
conn->smp = NULL;
memset(smp, 0, sizeof(*smp));
if (pairing_active && g_smp_auth_cb && g_smp_auth_cb->pairing_cancel)
{
g_smp_auth_cb->pairing_cancel(conn);
}
}
static void bt_smp_encrypt_change(FAR struct bt_conn_s *conn,
@@ -1579,6 +1944,11 @@ static int smp_self_test(void)
* Public Functions
****************************************************************************/
FAR void bt_smp_auth_cb_register(const struct bt_smp_auth_cb_s *cb)
{
g_smp_auth_cb = cb;
}
int bt_smp_initialize(void)
{
static struct bt_l2cap_chan_s chan =
@@ -1602,7 +1972,7 @@ int bt_smp_send_security_req(FAR struct bt_conn_s *conn)
FAR struct bt_smp_security_request_s *req;
FAR struct bt_buf_s *req_buf;
wlinfo("\n");
wlinfo("security req\n");
req_buf = bt_smp_create_pdu(conn, BT_SMP_CMD_SECURITY_REQUEST,
sizeof(struct bt_smp_security_request_s));
@@ -1613,6 +1983,12 @@ int bt_smp_send_security_req(FAR struct bt_conn_s *conn)
req = bt_buf_extend(req_buf, sizeof(struct bt_smp_security_request_s));
req->auth_req = BT_SMP_AUTH_BONDING;
if (conn->sec_level >= BT_SECURITY_HIGH &&
CONFIG_BLUETOOTH_SMP_IO_CAPABILITY != BT_SMP_IO_NO_INPUT_OUTPUT)
{
req->auth_req |= BT_SMP_AUTH_MITM;
}
bt_l2cap_send(conn, BT_L2CAP_CID_SMP, req_buf);
return 0;
@@ -1639,23 +2015,19 @@ int bt_smp_send_pairing_req(FAR struct bt_conn_s *conn)
req = bt_buf_extend(req_buf, sizeof(*req));
/* For JustWorks pairing simplify req parameters.
* TODO: needs to be reworked later on
*/
req->io_capability = CONFIG_BLUETOOTH_SMP_IO_CAPABILITY;
req->auth_req = BT_SMP_AUTH_BONDING;
req->io_capability = BT_SMP_IO_NO_INPUT_OUTPUT;
if (CONFIG_BLUETOOTH_SMP_IO_CAPABILITY != BT_SMP_IO_NO_INPUT_OUTPUT)
{
req->auth_req |= BT_SMP_AUTH_MITM;
}
req->oob_flag = BT_SMP_OOB_NOT_PRESENT;
req->max_key_size = BT_SMP_MAX_ENC_KEY_SIZE;
req->init_key_dist = SEND_KEYS;
req->resp_key_dist = RECV_KEYS;
smp->local_dist = SEND_KEYS;
smp->remote_dist = RECV_KEYS;
memset(smp->tk, 0, sizeof(smp->tk));
/* Store req for later use */
smp->local_dist = req->init_key_dist;
smp->remote_dist = req->resp_key_dist;
smp->preq[0] = BT_SMP_CMD_PAIRING_REQ;

View File

@@ -163,6 +163,29 @@ begin_packed_struct struct bt_smp_security_request_s
uint8_t auth_req;
} end_packed_struct;
struct bt_smp_auth_cb_s
{
/* Callback for Passkey display */
void (*passkey_display)(FAR struct bt_conn_s *conn, unsigned int passkey);
/* Callback for notification of pairing cancellation */
void (*pairing_cancel)(FAR struct bt_conn_s *conn);
/* Callback for notification of pairing success
* 'bonded' is true if bonding keys were generated/stored
*/
void (*pairing_complete)(FAR struct bt_conn_s *conn, bool bonded);
/* Callback for notification of pairing failure
* 'reason' is the SMP error code (BT_SMP_ERR_*)
*/
void (*pairing_failed)(FAR struct bt_conn_s *conn, uint8_t reason);
};
/****************************************************************************
* Public Function Prototypes
****************************************************************************/
@@ -172,5 +195,6 @@ bool bt_smp_irk_matches(FAR const uint8_t irk[16],
int bt_smp_send_pairing_req(FAR struct bt_conn_s *conn);
int bt_smp_send_security_req(FAR struct bt_conn_s *conn);
int bt_smp_initialize(void);
FAR void bt_smp_auth_cb_register(const struct bt_smp_auth_cb_s *cb);
#endif /* __WIRELESS_BLUETOOTH_BT_SMP_H */