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 indicate to the Host when its buffers are nearly full, allowing the Host to
stop sending data until buffer space becomes available. 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" menu "Kernel Thread Configuration"
config BLUETOOTH_TXCMD_STACKSIZE 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, FAR const struct bt_gatt_attr_s *attr,
uint8_t mask) 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; 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; mask &= attr->perm;
if (mask & BT_GATT_PERM_AUTHEN_MASK) 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) 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; return -ENOTCONN;
} }
/* Store the requested security level */
conn->sec_level = sec;
/* Nothing to do */ /* Nothing to do */
if (sec == BT_SECURITY_LOW) 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; 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; return -EINVAL;
} }

View file

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

View file

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

View file

@ -71,6 +71,16 @@
/* SMP channel specific context */ /* 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 struct bt_smp_s
{ {
/* The connection this context is associated with */ /* The connection this context is associated with */
@ -85,6 +95,14 @@ struct bt_smp_s
bool pending_encrypt; bool pending_encrypt;
/* Selected pairing method */
enum smp_pairing_method_e selected_method;
/* Passkey */
uint32_t passkey;
/* Pairing Request PDU */ /* Pairing Request PDU */
uint8_t preq[7]; 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], static int smp_ah(FAR const uint8_t irk[16], FAR const uint8_t r[3],
FAR uint8_t out[3]); FAR uint8_t out[3]);
static int smp_c1(FAR const uint8_t k[16], FAR const uint8_t r[16], 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 const bt_addr_le_t *ia, FAR const bt_addr_le_t *ra,
FAR uint8_t enc_data[16]); FAR uint8_t enc_data[16]);
static int smp_s1(FAR const uint8_t k[16], FAR const uint8_t r1[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); FAR struct bt_buf_s *buf);
static uint8_t smp_security_request(FAR struct bt_conn_s *conn, static uint8_t smp_security_request(FAR struct bt_conn_s *conn,
FAR struct bt_buf_s *buf); 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, static void bt_smp_receive(FAR struct bt_conn_s *conn,
FAR struct bt_buf_s *buf, FAR void *context, FAR struct bt_buf_s *buf, FAR void *context,
uint16_t cid); 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 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[] = 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], 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 const bt_addr_le_t *ia, FAR const bt_addr_le_t *ra,
FAR uint8_t enc_data[16]) 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("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("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[0] = ia->type;
p1[1] = ra->type; p1[1] = ra->type;
memcpy(p1 + 2, preq, 7); memcpy(p1 + 2, preq, 7);
memcpy(p1 + 9, pres, 7); memcpy(p1 + 9, presp, 7);
wlinfo("p1 %s\n", h(p1, 16)); 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; rsp->reason = reason;
bt_l2cap_send(conn, BT_L2CAP_CID_SMP, buf); 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) static int smp_init(struct bt_smp_s *smp)
@ -576,6 +637,62 @@ static int smp_init(struct bt_smp_s *smp)
return 0; 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, static uint8_t smp_pairing_req(FAR struct bt_conn_s *conn,
FAR struct bt_buf_s *buf) 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_smp_pairing_s *rsp;
FAR struct bt_buf_s *rsp_buf; FAR struct bt_buf_s *rsp_buf;
FAR struct bt_smp_s *smp = conn->smp; 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; 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) || if ((req->max_key_size > BT_SMP_MAX_ENC_KEY_SIZE) ||
(req->max_key_size < BT_SMP_MIN_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; 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)); rsp_buf = bt_smp_create_pdu(conn, BT_SMP_CMD_PAIRING_RSP, sizeof(*rsp));
if (!rsp_buf) 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)); rsp = bt_buf_extend(rsp_buf, sizeof(*rsp));
/* For JustWorks pairing simplify rsp parameters. rsp->io_capability = local_io_cap;
* TODO: needs to be reworked later on. rsp->auth_req = local_auth_req;
*/
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->oob_flag = BT_SMP_OOB_NOT_PRESENT; 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->init_key_dist = (req->init_key_dist & RECV_KEYS);
rsp->resp_key_dist = (req->resp_key_dist & SEND_KEYS); rsp->resp_key_dist = (req->resp_key_dist & SEND_KEYS);
smp->local_dist = rsp->resp_key_dist; smp->local_dist = rsp->resp_key_dist;
smp->remote_dist = rsp->init_key_dist; smp->remote_dist = rsp->init_key_dist;
memset(smp->tk, 0, sizeof(smp->tk));
/* Store req/rsp for later use */ /* Store req/rsp for later use */
smp->preq[0] = BT_SMP_CMD_PAIRING_REQ; 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_pairing_s *rsp = (FAR void *)buf->data;
struct bt_smp_s *smp = conn->smp; 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) || if ((rsp->max_key_size > BT_SMP_MAX_ENC_KEY_SIZE) ||
(rsp->max_key_size < BT_SMP_MIN_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; 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->local_dist &= rsp->init_key_dist;
smp->remote_dist &= rsp->resp_key_dist; smp->remote_dist &= rsp->resp_key_dist;
/* Store rsp for later use */
smp->prsp[0] = BT_SMP_CMD_PAIRING_RSP; smp->prsp[0] = BT_SMP_CMD_PAIRING_RSP;
memcpy(smp->prsp + 1, rsp, sizeof(*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_smp_s *smp = conn->smp;
FAR struct bt_keys_s *keys; FAR struct bt_keys_s *keys;
uint8_t cfm[16]; uint8_t cfm[16];
enum bt_security_e pairing_sec_level;
int err; int err;
wlinfo("\n"); wlinfo("Received Pairing Random\n");
memcpy(smp->rrnd, req->val, sizeof(smp->rrnd)); 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))) if (memcmp(smp->pcnf, cfm, sizeof(smp->pcnf)))
{ {
wlerr("ERROR: Pairing Confirm verification failed!\n");
return BT_SMP_ERR_CONFIRM_FAILED; 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) if (conn->role == BT_HCI_ROLE_MASTER)
{ {
uint8_t stk[16]; uint8_t stk[16];
/* No need to store master STK */
err = smp_s1(smp->tk, smp->rrnd, smp->prnd, stk); err = smp_s1(smp->tk, smp->rrnd, smp->prnd, stk);
if (err) if (err)
{ {
return BT_SMP_ERR_UNSPECIFIED; 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)) 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; return BT_SMP_ERR_UNSPECIFIED;
} }
smp->pending_encrypt = true; smp->pending_encrypt = true;
bt_atomic_set(&smp->allowed_cmds, 0);
bt_atomic_setbit(&smp->allowed_cmds, BT_SMP_CMD_PAIRING_FAIL);
return 0; return 0;
} }
else
keys = bt_keys_get_type(BT_KEYS_SLAVE_LTK, &conn->dst);
if (keys == NULL)
{ {
wlerr("ERROR: Unable to create new keys\n"); /* Slave Role: Generate and store STK as the Slave LTK */
return BT_SMP_ERR_UNSPECIFIED;
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, 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); wlerr("ERROR: reason 0x%x\n", req->reason);
UNUSED(req); 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_set(&smp->allowed_cmds, 0);
bt_atomic_setbit(&smp->allowed_cmds, BT_SMP_CMD_PAIRING_FAIL); 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; 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) static void bt_smp_distribute_keys(FAR struct bt_conn_s *conn)
{ {
FAR struct bt_smp_s *smp = conn->smp; 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; ident->ediv = keys->slave_ltk.ediv;
bt_l2cap_send(conn, BT_L2CAP_CID_SMP, buf); 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_smp_s *smp = conn->smp;
FAR struct bt_keys_s *keys; 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); keys = bt_keys_get_type(BT_KEYS_LTK, &conn->dst);
if (!keys) 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); memcpy(keys->ltk.val, req->ltk, 16);
keys->ltk.level = conn->sec_level;
bt_atomic_setbit(&smp->allowed_cmds, BT_SMP_CMD_MASTER_IDENT); 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_smp_s *smp = conn->smp;
FAR struct bt_keys_s *keys; 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); keys = bt_keys_get_type(BT_KEYS_LTK, &conn->dst);
if (!keys) if (!keys)
@ -968,17 +1277,34 @@ static uint8_t smp_master_ident(FAR struct bt_conn_s *conn,
return BT_SMP_ERR_UNSPECIFIED; return BT_SMP_ERR_UNSPECIFIED;
} }
if (!(keys->keys & BT_KEYS_LTK))
{
return BT_SMP_ERR_UNSPECIFIED;
}
keys->ltk.ediv = req->ediv; keys->ltk.ediv = req->ediv;
keys->ltk.rand = req->rand; 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->local_dist && !smp->remote_dist)
if (!smp->remote_dist)
{ {
wlinfo("Slave distributing keys now.\n");
bt_smp_distribute_keys(conn); 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)) if (!bt_addr_le_is_identity(&req->addr))
{ {
wlerr("ERROR: Invalid identity %s for %s\n", 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; 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); 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->local_dist && !smp->remote_dist)
if (!smp->remote_dist)
{ {
wlinfo("Slave distributing keys now.\n");
bt_smp_distribute_keys(conn); 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; 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_smp_security_request_s *req = (FAR void *)buf->data;
FAR struct bt_keys_s *keys; 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); 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; 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: pair:
if (bt_smp_send_pairing_req(conn) < 0) 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 void *context, uint16_t cid)
{ {
FAR struct bt_smp_s *smp = conn->smp; FAR struct bt_smp_s *smp = conn->smp;
bool pairing_active =
(smp && smp->selected_method != PAIRING_METHOD_JUST_WORKS);
if (!smp) if (!smp)
{ {
@ -1208,6 +1568,11 @@ static void bt_smp_disconnected(FAR struct bt_conn_s *conn,
conn->smp = NULL; conn->smp = NULL;
memset(smp, 0, sizeof(*smp)); 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, static void bt_smp_encrypt_change(FAR struct bt_conn_s *conn,
@ -1579,6 +1944,11 @@ static int smp_self_test(void)
* Public Functions * 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) int bt_smp_initialize(void)
{ {
static struct bt_l2cap_chan_s chan = 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_smp_security_request_s *req;
FAR struct bt_buf_s *req_buf; 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, req_buf = bt_smp_create_pdu(conn, BT_SMP_CMD_SECURITY_REQUEST,
sizeof(struct bt_smp_security_request_s)); 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 = bt_buf_extend(req_buf, sizeof(struct bt_smp_security_request_s));
req->auth_req = BT_SMP_AUTH_BONDING; 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); bt_l2cap_send(conn, BT_L2CAP_CID_SMP, req_buf);
return 0; 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)); req = bt_buf_extend(req_buf, sizeof(*req));
/* For JustWorks pairing simplify req parameters. req->io_capability = CONFIG_BLUETOOTH_SMP_IO_CAPABILITY;
* TODO: needs to be reworked later on
*/
req->auth_req = BT_SMP_AUTH_BONDING; 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->oob_flag = BT_SMP_OOB_NOT_PRESENT;
req->max_key_size = BT_SMP_MAX_ENC_KEY_SIZE; req->max_key_size = BT_SMP_MAX_ENC_KEY_SIZE;
req->init_key_dist = SEND_KEYS; req->init_key_dist = SEND_KEYS;
req->resp_key_dist = RECV_KEYS; req->resp_key_dist = RECV_KEYS;
smp->local_dist = req->init_key_dist;
smp->local_dist = SEND_KEYS; smp->remote_dist = req->resp_key_dist;
smp->remote_dist = RECV_KEYS;
memset(smp->tk, 0, sizeof(smp->tk));
/* Store req for later use */
smp->preq[0] = BT_SMP_CMD_PAIRING_REQ; 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; uint8_t auth_req;
} end_packed_struct; } 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 * 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_pairing_req(FAR struct bt_conn_s *conn);
int bt_smp_send_security_req(FAR struct bt_conn_s *conn); int bt_smp_send_security_req(FAR struct bt_conn_s *conn);
int bt_smp_initialize(void); 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 */ #endif /* __WIRELESS_BLUETOOTH_BT_SMP_H */