diff --git a/wireless/bluetooth/Kconfig b/wireless/bluetooth/Kconfig index 7ab4e09340499..97fd7802e8d7e 100644 --- a/wireless/bluetooth/Kconfig +++ b/wireless/bluetooth/Kconfig @@ -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 diff --git a/wireless/bluetooth/bt_att.c b/wireless/bluetooth/bt_att.c index 11ffa3a8b70d2..d3b38ff7489ff 100644 --- a/wireless/bluetooth/bt_att.c +++ b/wireless/bluetooth/bt_att.c @@ -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) diff --git a/wireless/bluetooth/bt_conn.c b/wireless/bluetooth/bt_conn.c index 264b1c6fee2b9..ca5e94fbbb8e4 100644 --- a/wireless/bluetooth/bt_conn.c +++ b/wireless/bluetooth/bt_conn.c @@ -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; } diff --git a/wireless/bluetooth/bt_conn.h b/wireless/bluetooth/bt_conn.h index 2cedff50a9f53..56ea7eaf7fc92 100644 --- a/wireless/bluetooth/bt_conn.h +++ b/wireless/bluetooth/bt_conn.h @@ -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; diff --git a/wireless/bluetooth/bt_keys.h b/wireless/bluetooth/bt_keys.h index 1f8122b2883ff..b04fbbf3a6e47 100644 --- a/wireless/bluetooth/bt_keys.h +++ b/wireless/bluetooth/bt_keys.h @@ -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; }; diff --git a/wireless/bluetooth/bt_smp.c b/wireless/bluetooth/bt_smp.c index 932bb15080859..7a8e3e2fa6e89 100644 --- a/wireless/bluetooth/bt_smp.c +++ b/wireless/bluetooth/bt_smp.c @@ -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->local_dist &= rsp->init_key_dist; - smp->remote_dist &= rsp->resp_key_dist; + 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); - /* Store rsp for later use */ + 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; 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 */ - 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; - } + 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; + } - /* Rand and EDiv are 0 for the STK */ + keys->slave_ltk.level = pairing_sec_level; - keys->slave_ltk.rand = 0; - keys->slave_ltk.ediv = 0; + /* Rand and EDiv are 0 for the STK */ - wlinfo("generated STK %s\n", h(keys->slave_ltk.val, 16)); + keys->slave_ltk.rand = 0; + keys->slave_ltk.ediv = 0; - smp->pending_encrypt = true; + wlinfo("Slave generated STK/SlaveLTK: %s (level %d)\n", + h(keys->slave_ltk.val, 16), keys->slave_ltk.level); - smp_send_pairing_random(conn); + smp->pending_encrypt = true; - return 0; + 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; + } } 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) { - goto pair; - } + bool mitm_required_by_slave = (slave_auth_req & BT_SMP_AUTH_MITM); + bool key_has_mitm = (keys->ltk.level >= BT_SECURITY_HIGH); - 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; - } + 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. */ - if (bt_conn_le_start_encryption(conn, keys->ltk.rand, keys->ltk.ediv, - keys->ltk.val) < 0) + 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 { - return BT_SMP_ERR_UNSPECIFIED; + wlinfo("No existing keys found.\n"); + goto pair; } - 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; diff --git a/wireless/bluetooth/bt_smp.h b/wireless/bluetooth/bt_smp.h index 13919bd603cda..f68642b70e4e7 100644 --- a/wireless/bluetooth/bt_smp.h +++ b/wireless/bluetooth/bt_smp.h @@ -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 */