Bluetooth: host: Split SSP to it's own module
Refactor the BR/EDR SSP feature out into it's own source file. Signed-off-by: Joakim Andersson <joakim.andersson@nordicsemi.no>
This commit is contained in:
parent
487d2be749
commit
b0d8758fb6
7 changed files with 841 additions and 770 deletions
|
@ -18,6 +18,7 @@ zephyr_library_sources_ifdef(
|
||||||
keys_br.c
|
keys_br.c
|
||||||
l2cap_br.c
|
l2cap_br.c
|
||||||
sdp.c
|
sdp.c
|
||||||
|
ssp.c
|
||||||
)
|
)
|
||||||
|
|
||||||
zephyr_library_sources_ifdef(
|
zephyr_library_sources_ifdef(
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
#include "l2cap_internal.h"
|
#include "l2cap_internal.h"
|
||||||
#include "keys.h"
|
#include "keys.h"
|
||||||
#include "smp.h"
|
#include "smp.h"
|
||||||
|
#include "ssp.h"
|
||||||
#include "att_internal.h"
|
#include "att_internal.h"
|
||||||
#include "gatt_internal.h"
|
#include "gatt_internal.h"
|
||||||
|
|
||||||
|
@ -76,22 +77,6 @@ K_FIFO_DEFINE(free_tx);
|
||||||
|
|
||||||
#if defined(CONFIG_BT_BREDR)
|
#if defined(CONFIG_BT_BREDR)
|
||||||
static struct bt_conn sco_conns[CONFIG_BT_MAX_SCO_CONN];
|
static struct bt_conn sco_conns[CONFIG_BT_MAX_SCO_CONN];
|
||||||
|
|
||||||
enum pairing_method {
|
|
||||||
LEGACY, /* Legacy (pre-SSP) pairing */
|
|
||||||
JUST_WORKS, /* JustWorks pairing */
|
|
||||||
PASSKEY_INPUT, /* Passkey Entry input */
|
|
||||||
PASSKEY_DISPLAY, /* Passkey Entry display */
|
|
||||||
PASSKEY_CONFIRM, /* Passkey confirm */
|
|
||||||
};
|
|
||||||
|
|
||||||
/* based on table 5.7, Core Spec 4.2, Vol.3 Part C, 5.2.2.6 */
|
|
||||||
static const uint8_t ssp_method[4 /* remote */][4 /* local */] = {
|
|
||||||
{ JUST_WORKS, JUST_WORKS, PASSKEY_INPUT, JUST_WORKS },
|
|
||||||
{ JUST_WORKS, PASSKEY_CONFIRM, PASSKEY_INPUT, JUST_WORKS },
|
|
||||||
{ PASSKEY_DISPLAY, PASSKEY_DISPLAY, PASSKEY_INPUT, JUST_WORKS },
|
|
||||||
{ JUST_WORKS, JUST_WORKS, JUST_WORKS, JUST_WORKS },
|
|
||||||
};
|
|
||||||
#endif /* CONFIG_BT_BREDR */
|
#endif /* CONFIG_BT_BREDR */
|
||||||
|
|
||||||
struct k_sem *bt_conn_get_pkts(struct bt_conn *conn)
|
struct k_sem *bt_conn_get_pkts(struct bt_conn *conn)
|
||||||
|
@ -659,279 +644,6 @@ struct bt_conn *bt_conn_add_br(const bt_addr_t *peer)
|
||||||
return conn;
|
return conn;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int pin_code_neg_reply(const bt_addr_t *bdaddr)
|
|
||||||
{
|
|
||||||
struct bt_hci_cp_pin_code_neg_reply *cp;
|
|
||||||
struct net_buf *buf;
|
|
||||||
|
|
||||||
BT_DBG("");
|
|
||||||
|
|
||||||
buf = bt_hci_cmd_create(BT_HCI_OP_PIN_CODE_NEG_REPLY, sizeof(*cp));
|
|
||||||
if (!buf) {
|
|
||||||
return -ENOBUFS;
|
|
||||||
}
|
|
||||||
|
|
||||||
cp = net_buf_add(buf, sizeof(*cp));
|
|
||||||
bt_addr_copy(&cp->bdaddr, bdaddr);
|
|
||||||
|
|
||||||
return bt_hci_cmd_send_sync(BT_HCI_OP_PIN_CODE_NEG_REPLY, buf, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int pin_code_reply(struct bt_conn *conn, const char *pin, uint8_t len)
|
|
||||||
{
|
|
||||||
struct bt_hci_cp_pin_code_reply *cp;
|
|
||||||
struct net_buf *buf;
|
|
||||||
|
|
||||||
BT_DBG("");
|
|
||||||
|
|
||||||
buf = bt_hci_cmd_create(BT_HCI_OP_PIN_CODE_REPLY, sizeof(*cp));
|
|
||||||
if (!buf) {
|
|
||||||
return -ENOBUFS;
|
|
||||||
}
|
|
||||||
|
|
||||||
cp = net_buf_add(buf, sizeof(*cp));
|
|
||||||
|
|
||||||
bt_addr_copy(&cp->bdaddr, &conn->br.dst);
|
|
||||||
cp->pin_len = len;
|
|
||||||
strncpy((char *)cp->pin_code, pin, sizeof(cp->pin_code));
|
|
||||||
|
|
||||||
return bt_hci_cmd_send_sync(BT_HCI_OP_PIN_CODE_REPLY, buf, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
int bt_conn_auth_pincode_entry(struct bt_conn *conn, const char *pin)
|
|
||||||
{
|
|
||||||
size_t len;
|
|
||||||
|
|
||||||
if (!bt_auth) {
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (conn->type != BT_CONN_TYPE_BR) {
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
len = strlen(pin);
|
|
||||||
if (len > 16) {
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (conn->required_sec_level == BT_SECURITY_L3 && len < 16) {
|
|
||||||
BT_WARN("PIN code for %s is not 16 bytes wide",
|
|
||||||
bt_addr_str(&conn->br.dst));
|
|
||||||
return -EPERM;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Allow user send entered PIN to remote, then reset user state. */
|
|
||||||
if (!atomic_test_and_clear_bit(conn->flags, BT_CONN_USER)) {
|
|
||||||
return -EPERM;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (len == 16) {
|
|
||||||
atomic_set_bit(conn->flags, BT_CONN_BR_LEGACY_SECURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
return pin_code_reply(conn, pin, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
void bt_conn_pin_code_req(struct bt_conn *conn)
|
|
||||||
{
|
|
||||||
if (bt_auth && bt_auth->pincode_entry) {
|
|
||||||
bool secure = false;
|
|
||||||
|
|
||||||
if (conn->required_sec_level == BT_SECURITY_L3) {
|
|
||||||
secure = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
atomic_set_bit(conn->flags, BT_CONN_USER);
|
|
||||||
atomic_set_bit(conn->flags, BT_CONN_BR_PAIRING);
|
|
||||||
bt_auth->pincode_entry(conn, secure);
|
|
||||||
} else {
|
|
||||||
pin_code_neg_reply(&conn->br.dst);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t bt_conn_get_io_capa(void)
|
|
||||||
{
|
|
||||||
if (!bt_auth) {
|
|
||||||
return BT_IO_NO_INPUT_OUTPUT;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bt_auth->passkey_confirm && bt_auth->passkey_display) {
|
|
||||||
return BT_IO_DISPLAY_YESNO;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bt_auth->passkey_entry) {
|
|
||||||
return BT_IO_KEYBOARD_ONLY;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bt_auth->passkey_display) {
|
|
||||||
return BT_IO_DISPLAY_ONLY;
|
|
||||||
}
|
|
||||||
|
|
||||||
return BT_IO_NO_INPUT_OUTPUT;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint8_t ssp_pair_method(const struct bt_conn *conn)
|
|
||||||
{
|
|
||||||
return ssp_method[conn->br.remote_io_capa][bt_conn_get_io_capa()];
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t bt_conn_ssp_get_auth(const struct bt_conn *conn)
|
|
||||||
{
|
|
||||||
/* Validate no bond auth request, and if valid use it. */
|
|
||||||
if ((conn->br.remote_auth == BT_HCI_NO_BONDING) ||
|
|
||||||
((conn->br.remote_auth == BT_HCI_NO_BONDING_MITM) &&
|
|
||||||
(ssp_pair_method(conn) > JUST_WORKS))) {
|
|
||||||
return conn->br.remote_auth;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Local & remote have enough IO capabilities to get MITM protection. */
|
|
||||||
if (ssp_pair_method(conn) > JUST_WORKS) {
|
|
||||||
return conn->br.remote_auth | BT_MITM;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* No MITM protection possible so ignore remote MITM requirement. */
|
|
||||||
return (conn->br.remote_auth & ~BT_MITM);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int ssp_confirm_reply(struct bt_conn *conn)
|
|
||||||
{
|
|
||||||
struct bt_hci_cp_user_confirm_reply *cp;
|
|
||||||
struct net_buf *buf;
|
|
||||||
|
|
||||||
BT_DBG("");
|
|
||||||
|
|
||||||
buf = bt_hci_cmd_create(BT_HCI_OP_USER_CONFIRM_REPLY, sizeof(*cp));
|
|
||||||
if (!buf) {
|
|
||||||
return -ENOBUFS;
|
|
||||||
}
|
|
||||||
|
|
||||||
cp = net_buf_add(buf, sizeof(*cp));
|
|
||||||
bt_addr_copy(&cp->bdaddr, &conn->br.dst);
|
|
||||||
|
|
||||||
return bt_hci_cmd_send_sync(BT_HCI_OP_USER_CONFIRM_REPLY, buf, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int ssp_confirm_neg_reply(struct bt_conn *conn)
|
|
||||||
{
|
|
||||||
struct bt_hci_cp_user_confirm_reply *cp;
|
|
||||||
struct net_buf *buf;
|
|
||||||
|
|
||||||
BT_DBG("");
|
|
||||||
|
|
||||||
buf = bt_hci_cmd_create(BT_HCI_OP_USER_CONFIRM_NEG_REPLY, sizeof(*cp));
|
|
||||||
if (!buf) {
|
|
||||||
return -ENOBUFS;
|
|
||||||
}
|
|
||||||
|
|
||||||
cp = net_buf_add(buf, sizeof(*cp));
|
|
||||||
bt_addr_copy(&cp->bdaddr, &conn->br.dst);
|
|
||||||
|
|
||||||
return bt_hci_cmd_send_sync(BT_HCI_OP_USER_CONFIRM_NEG_REPLY, buf,
|
|
||||||
NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
void bt_conn_ssp_auth_complete(struct bt_conn *conn, uint8_t status)
|
|
||||||
{
|
|
||||||
if (!status) {
|
|
||||||
bool bond = !atomic_test_bit(conn->flags, BT_CONN_BR_NOBOND);
|
|
||||||
|
|
||||||
if (bt_auth && bt_auth->pairing_complete) {
|
|
||||||
bt_auth->pairing_complete(conn, bond);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (bt_auth && bt_auth->pairing_failed) {
|
|
||||||
bt_auth->pairing_failed(conn, status);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void bt_conn_ssp_auth(struct bt_conn *conn, uint32_t passkey)
|
|
||||||
{
|
|
||||||
conn->br.pairing_method = ssp_pair_method(conn);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If local required security is HIGH then MITM is mandatory.
|
|
||||||
* MITM protection is no achievable when SSP 'justworks' is applied.
|
|
||||||
*/
|
|
||||||
if (conn->required_sec_level > BT_SECURITY_L2 &&
|
|
||||||
conn->br.pairing_method == JUST_WORKS) {
|
|
||||||
BT_DBG("MITM protection infeasible for required security");
|
|
||||||
ssp_confirm_neg_reply(conn);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (conn->br.pairing_method) {
|
|
||||||
case PASSKEY_CONFIRM:
|
|
||||||
atomic_set_bit(conn->flags, BT_CONN_USER);
|
|
||||||
bt_auth->passkey_confirm(conn, passkey);
|
|
||||||
break;
|
|
||||||
case PASSKEY_DISPLAY:
|
|
||||||
atomic_set_bit(conn->flags, BT_CONN_USER);
|
|
||||||
bt_auth->passkey_display(conn, passkey);
|
|
||||||
break;
|
|
||||||
case PASSKEY_INPUT:
|
|
||||||
atomic_set_bit(conn->flags, BT_CONN_USER);
|
|
||||||
bt_auth->passkey_entry(conn);
|
|
||||||
break;
|
|
||||||
case JUST_WORKS:
|
|
||||||
/*
|
|
||||||
* When local host works as pairing acceptor and 'justworks'
|
|
||||||
* model is applied then notify user about such pairing request.
|
|
||||||
* [BT Core 4.2 table 5.7, Vol 3, Part C, 5.2.2.6]
|
|
||||||
*/
|
|
||||||
if (bt_auth && bt_auth->pairing_confirm &&
|
|
||||||
!atomic_test_bit(conn->flags,
|
|
||||||
BT_CONN_BR_PAIRING_INITIATOR)) {
|
|
||||||
atomic_set_bit(conn->flags, BT_CONN_USER);
|
|
||||||
bt_auth->pairing_confirm(conn);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
ssp_confirm_reply(conn);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int ssp_passkey_reply(struct bt_conn *conn, unsigned int passkey)
|
|
||||||
{
|
|
||||||
struct bt_hci_cp_user_passkey_reply *cp;
|
|
||||||
struct net_buf *buf;
|
|
||||||
|
|
||||||
BT_DBG("");
|
|
||||||
|
|
||||||
buf = bt_hci_cmd_create(BT_HCI_OP_USER_PASSKEY_REPLY, sizeof(*cp));
|
|
||||||
if (!buf) {
|
|
||||||
return -ENOBUFS;
|
|
||||||
}
|
|
||||||
|
|
||||||
cp = net_buf_add(buf, sizeof(*cp));
|
|
||||||
bt_addr_copy(&cp->bdaddr, &conn->br.dst);
|
|
||||||
cp->passkey = sys_cpu_to_le32(passkey);
|
|
||||||
|
|
||||||
return bt_hci_cmd_send_sync(BT_HCI_OP_USER_PASSKEY_REPLY, buf, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int ssp_passkey_neg_reply(struct bt_conn *conn)
|
|
||||||
{
|
|
||||||
struct bt_hci_cp_user_passkey_neg_reply *cp;
|
|
||||||
struct net_buf *buf;
|
|
||||||
|
|
||||||
BT_DBG("");
|
|
||||||
|
|
||||||
buf = bt_hci_cmd_create(BT_HCI_OP_USER_PASSKEY_NEG_REPLY, sizeof(*cp));
|
|
||||||
if (!buf) {
|
|
||||||
return -ENOBUFS;
|
|
||||||
}
|
|
||||||
|
|
||||||
cp = net_buf_add(buf, sizeof(*cp));
|
|
||||||
bt_addr_copy(&cp->bdaddr, &conn->br.dst);
|
|
||||||
|
|
||||||
return bt_hci_cmd_send_sync(BT_HCI_OP_USER_PASSKEY_NEG_REPLY, buf,
|
|
||||||
NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int bt_hci_connect_br_cancel(struct bt_conn *conn)
|
static int bt_hci_connect_br_cancel(struct bt_conn *conn)
|
||||||
{
|
{
|
||||||
struct bt_hci_cp_connect_cancel *cp;
|
struct bt_hci_cp_connect_cancel *cp;
|
||||||
|
@ -961,25 +673,6 @@ static int bt_hci_connect_br_cancel(struct bt_conn *conn)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int conn_auth(struct bt_conn *conn)
|
|
||||||
{
|
|
||||||
struct bt_hci_cp_auth_requested *auth;
|
|
||||||
struct net_buf *buf;
|
|
||||||
|
|
||||||
BT_DBG("");
|
|
||||||
|
|
||||||
buf = bt_hci_cmd_create(BT_HCI_OP_AUTH_REQUESTED, sizeof(*auth));
|
|
||||||
if (!buf) {
|
|
||||||
return -ENOBUFS;
|
|
||||||
}
|
|
||||||
|
|
||||||
auth = net_buf_add(buf, sizeof(*auth));
|
|
||||||
auth->handle = sys_cpu_to_le16(conn->handle);
|
|
||||||
|
|
||||||
atomic_set_bit(conn->flags, BT_CONN_BR_PAIRING_INITIATOR);
|
|
||||||
|
|
||||||
return bt_hci_cmd_send_sync(BT_HCI_OP_AUTH_REQUESTED, buf, NULL);
|
|
||||||
}
|
|
||||||
#endif /* CONFIG_BT_BREDR */
|
#endif /* CONFIG_BT_BREDR */
|
||||||
|
|
||||||
#if defined(CONFIG_BT_SMP)
|
#if defined(CONFIG_BT_SMP)
|
||||||
|
@ -1116,24 +809,9 @@ void bt_conn_security_changed(struct bt_conn *conn, uint8_t hci_err,
|
||||||
|
|
||||||
static int start_security(struct bt_conn *conn)
|
static int start_security(struct bt_conn *conn)
|
||||||
{
|
{
|
||||||
#if defined(CONFIG_BT_BREDR)
|
if (IS_ENABLED(CONFIG_BT_BREDR) && conn->type == BT_CONN_TYPE_BR) {
|
||||||
if (conn->type == BT_CONN_TYPE_BR) {
|
return bt_ssp_start_security(conn);
|
||||||
if (atomic_test_bit(conn->flags, BT_CONN_BR_PAIRING)) {
|
|
||||||
return -EBUSY;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (conn->required_sec_level > BT_SECURITY_L3) {
|
|
||||||
return -ENOTSUP;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bt_conn_get_io_capa() == BT_IO_NO_INPUT_OUTPUT &&
|
|
||||||
conn->required_sec_level > BT_SECURITY_L2) {
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return conn_auth(conn);
|
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_BT_BREDR */
|
|
||||||
|
|
||||||
if (IS_ENABLED(CONFIG_BT_SMP)) {
|
if (IS_ENABLED(CONFIG_BT_SMP)) {
|
||||||
return bt_smp_start_security(conn);
|
return bt_smp_start_security(conn);
|
||||||
|
@ -2661,18 +2339,9 @@ int bt_conn_auth_passkey_entry(struct bt_conn *conn, unsigned int passkey)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(CONFIG_BT_BREDR)
|
if (IS_ENABLED(CONFIG_BT_BREDR) && conn->type == BT_CONN_TYPE_BR) {
|
||||||
if (conn->type == BT_CONN_TYPE_BR) {
|
return bt_ssp_auth_passkey_entry(conn, passkey);
|
||||||
/* User entered passkey, reset user state. */
|
|
||||||
if (!atomic_test_and_clear_bit(conn->flags, BT_CONN_USER)) {
|
|
||||||
return -EPERM;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (conn->br.pairing_method == PASSKEY_INPUT) {
|
|
||||||
return ssp_passkey_reply(conn, passkey);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_BT_BREDR */
|
|
||||||
|
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
@ -2688,16 +2357,10 @@ int bt_conn_auth_passkey_confirm(struct bt_conn *conn)
|
||||||
return bt_smp_auth_passkey_confirm(conn);
|
return bt_smp_auth_passkey_confirm(conn);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(CONFIG_BT_BREDR)
|
if (IS_ENABLED(CONFIG_BT_BREDR) &&
|
||||||
if (conn->type == BT_CONN_TYPE_BR) {
|
conn->type == BT_CONN_TYPE_BR) {
|
||||||
/* Allow user confirm passkey value, then reset user state. */
|
return bt_ssp_auth_passkey_confirm(conn);
|
||||||
if (!atomic_test_and_clear_bit(conn->flags, BT_CONN_USER)) {
|
|
||||||
return -EPERM;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ssp_confirm_reply(conn);
|
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_BT_BREDR */
|
|
||||||
|
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
@ -2714,25 +2377,7 @@ int bt_conn_auth_cancel(struct bt_conn *conn)
|
||||||
|
|
||||||
#if defined(CONFIG_BT_BREDR)
|
#if defined(CONFIG_BT_BREDR)
|
||||||
if (conn->type == BT_CONN_TYPE_BR) {
|
if (conn->type == BT_CONN_TYPE_BR) {
|
||||||
/* Allow user cancel authentication, then reset user state. */
|
return bt_ssp_auth_cancel(conn);
|
||||||
if (!atomic_test_and_clear_bit(conn->flags, BT_CONN_USER)) {
|
|
||||||
return -EPERM;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (conn->br.pairing_method) {
|
|
||||||
case JUST_WORKS:
|
|
||||||
case PASSKEY_CONFIRM:
|
|
||||||
return ssp_confirm_neg_reply(conn);
|
|
||||||
case PASSKEY_INPUT:
|
|
||||||
return ssp_passkey_neg_reply(conn);
|
|
||||||
case PASSKEY_DISPLAY:
|
|
||||||
return bt_conn_disconnect(conn,
|
|
||||||
BT_HCI_ERR_AUTH_FAIL);
|
|
||||||
case LEGACY:
|
|
||||||
return pin_code_neg_reply(&conn->br.dst);
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_BT_BREDR */
|
#endif /* CONFIG_BT_BREDR */
|
||||||
|
|
||||||
|
@ -2752,7 +2397,7 @@ int bt_conn_auth_pairing_confirm(struct bt_conn *conn)
|
||||||
#endif /* CONFIG_BT_SMP */
|
#endif /* CONFIG_BT_SMP */
|
||||||
#if defined(CONFIG_BT_BREDR)
|
#if defined(CONFIG_BT_BREDR)
|
||||||
case BT_CONN_TYPE_BR:
|
case BT_CONN_TYPE_BR:
|
||||||
return ssp_confirm_reply(conn);
|
return bt_ssp_auth_pairing_confirm(conn);
|
||||||
#endif /* CONFIG_BT_BREDR */
|
#endif /* CONFIG_BT_BREDR */
|
||||||
default:
|
default:
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
|
@ -205,12 +205,6 @@ struct bt_conn *bt_conn_lookup_addr_sco(const bt_addr_t *peer);
|
||||||
/* Look up an existing connection by BT address */
|
/* Look up an existing connection by BT address */
|
||||||
struct bt_conn *bt_conn_lookup_addr_br(const bt_addr_t *peer);
|
struct bt_conn *bt_conn_lookup_addr_br(const bt_addr_t *peer);
|
||||||
|
|
||||||
void bt_conn_pin_code_req(struct bt_conn *conn);
|
|
||||||
uint8_t bt_conn_get_io_capa(void);
|
|
||||||
uint8_t bt_conn_ssp_get_auth(const struct bt_conn *conn);
|
|
||||||
void bt_conn_ssp_auth(struct bt_conn *conn, uint32_t passkey);
|
|
||||||
void bt_conn_ssp_auth_complete(struct bt_conn *conn, uint8_t status);
|
|
||||||
|
|
||||||
void bt_conn_disconnect_all(uint8_t id);
|
void bt_conn_disconnect_all(uint8_t id);
|
||||||
|
|
||||||
/* Look up an existing connection */
|
/* Look up an existing connection */
|
||||||
|
|
|
@ -2695,7 +2695,7 @@ int bt_unpair(uint8_t id, const bt_addr_le_t *addr)
|
||||||
#endif /* CONFIG_BT_CONN */
|
#endif /* CONFIG_BT_CONN */
|
||||||
|
|
||||||
#if defined(CONFIG_BT_SMP) || defined(CONFIG_BT_BREDR)
|
#if defined(CONFIG_BT_SMP) || defined(CONFIG_BT_BREDR)
|
||||||
static enum bt_security_err security_err_get(uint8_t hci_err)
|
enum bt_security_err bt_security_err_get(uint8_t hci_err)
|
||||||
{
|
{
|
||||||
switch (hci_err) {
|
switch (hci_err) {
|
||||||
case BT_HCI_ERR_SUCCESS:
|
case BT_HCI_ERR_SUCCESS:
|
||||||
|
@ -2993,344 +2993,6 @@ static void conn_complete(struct net_buf *buf)
|
||||||
bt_hci_cmd_send_sync(BT_HCI_OP_READ_REMOTE_FEATURES, buf, NULL);
|
bt_hci_cmd_send_sync(BT_HCI_OP_READ_REMOTE_FEATURES, buf, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pin_code_req(struct net_buf *buf)
|
|
||||||
{
|
|
||||||
struct bt_hci_evt_pin_code_req *evt = (void *)buf->data;
|
|
||||||
struct bt_conn *conn;
|
|
||||||
|
|
||||||
BT_DBG("");
|
|
||||||
|
|
||||||
conn = bt_conn_lookup_addr_br(&evt->bdaddr);
|
|
||||||
if (!conn) {
|
|
||||||
BT_ERR("Can't find conn for %s", bt_addr_str(&evt->bdaddr));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
bt_conn_pin_code_req(conn);
|
|
||||||
bt_conn_unref(conn);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void link_key_notify(struct net_buf *buf)
|
|
||||||
{
|
|
||||||
struct bt_hci_evt_link_key_notify *evt = (void *)buf->data;
|
|
||||||
struct bt_conn *conn;
|
|
||||||
|
|
||||||
conn = bt_conn_lookup_addr_br(&evt->bdaddr);
|
|
||||||
if (!conn) {
|
|
||||||
BT_ERR("Can't find conn for %s", bt_addr_str(&evt->bdaddr));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
BT_DBG("%s, link type 0x%02x", bt_addr_str(&evt->bdaddr), evt->key_type);
|
|
||||||
|
|
||||||
if (!conn->br.link_key) {
|
|
||||||
conn->br.link_key = bt_keys_get_link_key(&evt->bdaddr);
|
|
||||||
}
|
|
||||||
if (!conn->br.link_key) {
|
|
||||||
BT_ERR("Can't update keys for %s", bt_addr_str(&evt->bdaddr));
|
|
||||||
bt_conn_unref(conn);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* clear any old Link Key flags */
|
|
||||||
conn->br.link_key->flags = 0U;
|
|
||||||
|
|
||||||
switch (evt->key_type) {
|
|
||||||
case BT_LK_COMBINATION:
|
|
||||||
/*
|
|
||||||
* Setting Combination Link Key as AUTHENTICATED means it was
|
|
||||||
* successfully generated by 16 digits wide PIN code.
|
|
||||||
*/
|
|
||||||
if (atomic_test_and_clear_bit(conn->flags,
|
|
||||||
BT_CONN_BR_LEGACY_SECURE)) {
|
|
||||||
conn->br.link_key->flags |= BT_LINK_KEY_AUTHENTICATED;
|
|
||||||
}
|
|
||||||
memcpy(conn->br.link_key->val, evt->link_key, 16);
|
|
||||||
break;
|
|
||||||
case BT_LK_AUTH_COMBINATION_P192:
|
|
||||||
conn->br.link_key->flags |= BT_LINK_KEY_AUTHENTICATED;
|
|
||||||
/* fall through */
|
|
||||||
case BT_LK_UNAUTH_COMBINATION_P192:
|
|
||||||
/* Mark no-bond so that link-key is removed on disconnection */
|
|
||||||
if (bt_conn_ssp_get_auth(conn) < BT_HCI_DEDICATED_BONDING) {
|
|
||||||
atomic_set_bit(conn->flags, BT_CONN_BR_NOBOND);
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(conn->br.link_key->val, evt->link_key, 16);
|
|
||||||
break;
|
|
||||||
case BT_LK_AUTH_COMBINATION_P256:
|
|
||||||
conn->br.link_key->flags |= BT_LINK_KEY_AUTHENTICATED;
|
|
||||||
/* fall through */
|
|
||||||
case BT_LK_UNAUTH_COMBINATION_P256:
|
|
||||||
conn->br.link_key->flags |= BT_LINK_KEY_SC;
|
|
||||||
|
|
||||||
/* Mark no-bond so that link-key is removed on disconnection */
|
|
||||||
if (bt_conn_ssp_get_auth(conn) < BT_HCI_DEDICATED_BONDING) {
|
|
||||||
atomic_set_bit(conn->flags, BT_CONN_BR_NOBOND);
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(conn->br.link_key->val, evt->link_key, 16);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
BT_WARN("Unsupported Link Key type %u", evt->key_type);
|
|
||||||
(void)memset(conn->br.link_key->val, 0,
|
|
||||||
sizeof(conn->br.link_key->val));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IS_ENABLED(CONFIG_BT_SETTINGS) &&
|
|
||||||
!atomic_test_bit(conn->flags, BT_CONN_BR_NOBOND)) {
|
|
||||||
bt_keys_link_key_store(conn->br.link_key);
|
|
||||||
}
|
|
||||||
|
|
||||||
bt_conn_unref(conn);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void link_key_neg_reply(const bt_addr_t *bdaddr)
|
|
||||||
{
|
|
||||||
struct bt_hci_cp_link_key_neg_reply *cp;
|
|
||||||
struct net_buf *buf;
|
|
||||||
|
|
||||||
BT_DBG("");
|
|
||||||
|
|
||||||
buf = bt_hci_cmd_create(BT_HCI_OP_LINK_KEY_NEG_REPLY, sizeof(*cp));
|
|
||||||
if (!buf) {
|
|
||||||
BT_ERR("Out of command buffers");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
cp = net_buf_add(buf, sizeof(*cp));
|
|
||||||
bt_addr_copy(&cp->bdaddr, bdaddr);
|
|
||||||
bt_hci_cmd_send_sync(BT_HCI_OP_LINK_KEY_NEG_REPLY, buf, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void link_key_reply(const bt_addr_t *bdaddr, const uint8_t *lk)
|
|
||||||
{
|
|
||||||
struct bt_hci_cp_link_key_reply *cp;
|
|
||||||
struct net_buf *buf;
|
|
||||||
|
|
||||||
BT_DBG("");
|
|
||||||
|
|
||||||
buf = bt_hci_cmd_create(BT_HCI_OP_LINK_KEY_REPLY, sizeof(*cp));
|
|
||||||
if (!buf) {
|
|
||||||
BT_ERR("Out of command buffers");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
cp = net_buf_add(buf, sizeof(*cp));
|
|
||||||
bt_addr_copy(&cp->bdaddr, bdaddr);
|
|
||||||
memcpy(cp->link_key, lk, 16);
|
|
||||||
bt_hci_cmd_send_sync(BT_HCI_OP_LINK_KEY_REPLY, buf, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void link_key_req(struct net_buf *buf)
|
|
||||||
{
|
|
||||||
struct bt_hci_evt_link_key_req *evt = (void *)buf->data;
|
|
||||||
struct bt_conn *conn;
|
|
||||||
|
|
||||||
BT_DBG("%s", bt_addr_str(&evt->bdaddr));
|
|
||||||
|
|
||||||
conn = bt_conn_lookup_addr_br(&evt->bdaddr);
|
|
||||||
if (!conn) {
|
|
||||||
BT_ERR("Can't find conn for %s", bt_addr_str(&evt->bdaddr));
|
|
||||||
link_key_neg_reply(&evt->bdaddr);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!conn->br.link_key) {
|
|
||||||
conn->br.link_key = bt_keys_find_link_key(&evt->bdaddr);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!conn->br.link_key) {
|
|
||||||
link_key_neg_reply(&evt->bdaddr);
|
|
||||||
bt_conn_unref(conn);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Enforce regenerate by controller stronger link key since found one
|
|
||||||
* in database not covers requested security level.
|
|
||||||
*/
|
|
||||||
if (!(conn->br.link_key->flags & BT_LINK_KEY_AUTHENTICATED) &&
|
|
||||||
conn->required_sec_level > BT_SECURITY_L2) {
|
|
||||||
link_key_neg_reply(&evt->bdaddr);
|
|
||||||
bt_conn_unref(conn);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
link_key_reply(&evt->bdaddr, conn->br.link_key->val);
|
|
||||||
bt_conn_unref(conn);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void io_capa_neg_reply(const bt_addr_t *bdaddr, const uint8_t reason)
|
|
||||||
{
|
|
||||||
struct bt_hci_cp_io_capability_neg_reply *cp;
|
|
||||||
struct net_buf *resp_buf;
|
|
||||||
|
|
||||||
resp_buf = bt_hci_cmd_create(BT_HCI_OP_IO_CAPABILITY_NEG_REPLY,
|
|
||||||
sizeof(*cp));
|
|
||||||
if (!resp_buf) {
|
|
||||||
BT_ERR("Out of command buffers");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
cp = net_buf_add(resp_buf, sizeof(*cp));
|
|
||||||
bt_addr_copy(&cp->bdaddr, bdaddr);
|
|
||||||
cp->reason = reason;
|
|
||||||
bt_hci_cmd_send_sync(BT_HCI_OP_IO_CAPABILITY_NEG_REPLY, resp_buf, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void io_capa_resp(struct net_buf *buf)
|
|
||||||
{
|
|
||||||
struct bt_hci_evt_io_capa_resp *evt = (void *)buf->data;
|
|
||||||
struct bt_conn *conn;
|
|
||||||
|
|
||||||
BT_DBG("remote %s, IOcapa 0x%02x, auth 0x%02x",
|
|
||||||
bt_addr_str(&evt->bdaddr), evt->capability, evt->authentication);
|
|
||||||
|
|
||||||
if (evt->authentication > BT_HCI_GENERAL_BONDING_MITM) {
|
|
||||||
BT_ERR("Invalid remote authentication requirements");
|
|
||||||
io_capa_neg_reply(&evt->bdaddr,
|
|
||||||
BT_HCI_ERR_UNSUPP_FEATURE_PARAM_VAL);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (evt->capability > BT_IO_NO_INPUT_OUTPUT) {
|
|
||||||
BT_ERR("Invalid remote io capability requirements");
|
|
||||||
io_capa_neg_reply(&evt->bdaddr,
|
|
||||||
BT_HCI_ERR_UNSUPP_FEATURE_PARAM_VAL);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
conn = bt_conn_lookup_addr_br(&evt->bdaddr);
|
|
||||||
if (!conn) {
|
|
||||||
BT_ERR("Unable to find conn for %s", bt_addr_str(&evt->bdaddr));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
conn->br.remote_io_capa = evt->capability;
|
|
||||||
conn->br.remote_auth = evt->authentication;
|
|
||||||
atomic_set_bit(conn->flags, BT_CONN_BR_PAIRING);
|
|
||||||
bt_conn_unref(conn);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void io_capa_req(struct net_buf *buf)
|
|
||||||
{
|
|
||||||
struct bt_hci_evt_io_capa_req *evt = (void *)buf->data;
|
|
||||||
struct net_buf *resp_buf;
|
|
||||||
struct bt_conn *conn;
|
|
||||||
struct bt_hci_cp_io_capability_reply *cp;
|
|
||||||
uint8_t auth;
|
|
||||||
|
|
||||||
BT_DBG("");
|
|
||||||
|
|
||||||
conn = bt_conn_lookup_addr_br(&evt->bdaddr);
|
|
||||||
if (!conn) {
|
|
||||||
BT_ERR("Can't find conn for %s", bt_addr_str(&evt->bdaddr));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
resp_buf = bt_hci_cmd_create(BT_HCI_OP_IO_CAPABILITY_REPLY,
|
|
||||||
sizeof(*cp));
|
|
||||||
if (!resp_buf) {
|
|
||||||
BT_ERR("Out of command buffers");
|
|
||||||
bt_conn_unref(conn);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Set authentication requirements when acting as pairing initiator to
|
|
||||||
* 'dedicated bond' with MITM protection set if local IO capa
|
|
||||||
* potentially allows it, and for acceptor, based on local IO capa and
|
|
||||||
* remote's authentication set.
|
|
||||||
*/
|
|
||||||
if (atomic_test_bit(conn->flags, BT_CONN_BR_PAIRING_INITIATOR)) {
|
|
||||||
if (bt_conn_get_io_capa() != BT_IO_NO_INPUT_OUTPUT) {
|
|
||||||
auth = BT_HCI_DEDICATED_BONDING_MITM;
|
|
||||||
} else {
|
|
||||||
auth = BT_HCI_DEDICATED_BONDING;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
auth = bt_conn_ssp_get_auth(conn);
|
|
||||||
}
|
|
||||||
|
|
||||||
cp = net_buf_add(resp_buf, sizeof(*cp));
|
|
||||||
bt_addr_copy(&cp->bdaddr, &evt->bdaddr);
|
|
||||||
cp->capability = bt_conn_get_io_capa();
|
|
||||||
cp->authentication = auth;
|
|
||||||
cp->oob_data = 0U;
|
|
||||||
bt_hci_cmd_send_sync(BT_HCI_OP_IO_CAPABILITY_REPLY, resp_buf, NULL);
|
|
||||||
bt_conn_unref(conn);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ssp_complete(struct net_buf *buf)
|
|
||||||
{
|
|
||||||
struct bt_hci_evt_ssp_complete *evt = (void *)buf->data;
|
|
||||||
struct bt_conn *conn;
|
|
||||||
|
|
||||||
BT_DBG("status 0x%02x", evt->status);
|
|
||||||
|
|
||||||
conn = bt_conn_lookup_addr_br(&evt->bdaddr);
|
|
||||||
if (!conn) {
|
|
||||||
BT_ERR("Can't find conn for %s", bt_addr_str(&evt->bdaddr));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
bt_conn_ssp_auth_complete(conn, security_err_get(evt->status));
|
|
||||||
if (evt->status) {
|
|
||||||
bt_conn_disconnect(conn, BT_HCI_ERR_AUTH_FAIL);
|
|
||||||
}
|
|
||||||
|
|
||||||
bt_conn_unref(conn);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void user_confirm_req(struct net_buf *buf)
|
|
||||||
{
|
|
||||||
struct bt_hci_evt_user_confirm_req *evt = (void *)buf->data;
|
|
||||||
struct bt_conn *conn;
|
|
||||||
|
|
||||||
conn = bt_conn_lookup_addr_br(&evt->bdaddr);
|
|
||||||
if (!conn) {
|
|
||||||
BT_ERR("Can't find conn for %s", bt_addr_str(&evt->bdaddr));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
bt_conn_ssp_auth(conn, sys_le32_to_cpu(evt->passkey));
|
|
||||||
bt_conn_unref(conn);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void user_passkey_notify(struct net_buf *buf)
|
|
||||||
{
|
|
||||||
struct bt_hci_evt_user_passkey_notify *evt = (void *)buf->data;
|
|
||||||
struct bt_conn *conn;
|
|
||||||
|
|
||||||
BT_DBG("");
|
|
||||||
|
|
||||||
conn = bt_conn_lookup_addr_br(&evt->bdaddr);
|
|
||||||
if (!conn) {
|
|
||||||
BT_ERR("Can't find conn for %s", bt_addr_str(&evt->bdaddr));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
bt_conn_ssp_auth(conn, sys_le32_to_cpu(evt->passkey));
|
|
||||||
bt_conn_unref(conn);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void user_passkey_req(struct net_buf *buf)
|
|
||||||
{
|
|
||||||
struct bt_hci_evt_user_passkey_req *evt = (void *)buf->data;
|
|
||||||
struct bt_conn *conn;
|
|
||||||
|
|
||||||
conn = bt_conn_lookup_addr_br(&evt->bdaddr);
|
|
||||||
if (!conn) {
|
|
||||||
BT_ERR("Can't find conn for %s", bt_addr_str(&evt->bdaddr));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
bt_conn_ssp_auth(conn, 0);
|
|
||||||
bt_conn_unref(conn);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct discovery_priv {
|
struct discovery_priv {
|
||||||
uint16_t clock_offset;
|
uint16_t clock_offset;
|
||||||
uint8_t pscan_rep_mode;
|
uint8_t pscan_rep_mode;
|
||||||
|
@ -3644,54 +3306,6 @@ check_names:
|
||||||
discovery_results_count = 0;
|
discovery_results_count = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void link_encr(const uint16_t handle)
|
|
||||||
{
|
|
||||||
struct bt_hci_cp_set_conn_encrypt *encr;
|
|
||||||
struct net_buf *buf;
|
|
||||||
|
|
||||||
BT_DBG("");
|
|
||||||
|
|
||||||
buf = bt_hci_cmd_create(BT_HCI_OP_SET_CONN_ENCRYPT, sizeof(*encr));
|
|
||||||
if (!buf) {
|
|
||||||
BT_ERR("Out of command buffers");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
encr = net_buf_add(buf, sizeof(*encr));
|
|
||||||
encr->handle = sys_cpu_to_le16(handle);
|
|
||||||
encr->encrypt = 0x01;
|
|
||||||
|
|
||||||
bt_hci_cmd_send_sync(BT_HCI_OP_SET_CONN_ENCRYPT, buf, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void auth_complete(struct net_buf *buf)
|
|
||||||
{
|
|
||||||
struct bt_hci_evt_auth_complete *evt = (void *)buf->data;
|
|
||||||
struct bt_conn *conn;
|
|
||||||
uint16_t handle = sys_le16_to_cpu(evt->handle);
|
|
||||||
|
|
||||||
BT_DBG("status 0x%02x, handle %u", evt->status, handle);
|
|
||||||
|
|
||||||
conn = bt_conn_lookup_handle(handle);
|
|
||||||
if (!conn) {
|
|
||||||
BT_ERR("Can't find conn for handle %u", handle);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (evt->status) {
|
|
||||||
/*
|
|
||||||
* Inform layers above HCI about non-zero authentication
|
|
||||||
* status to make them able cleanup pending jobs.
|
|
||||||
*/
|
|
||||||
bt_conn_security_changed(conn, evt->status,
|
|
||||||
security_err_get(evt->status));
|
|
||||||
} else {
|
|
||||||
link_encr(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
bt_conn_unref(conn);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void read_remote_features_complete(struct net_buf *buf)
|
static void read_remote_features_complete(struct net_buf *buf)
|
||||||
{
|
{
|
||||||
struct bt_hci_evt_remote_features *evt = (void *)buf->data;
|
struct bt_hci_evt_remote_features *evt = (void *)buf->data;
|
||||||
|
@ -4131,7 +3745,7 @@ static void hci_encrypt_change(struct net_buf *buf)
|
||||||
|
|
||||||
if (evt->status) {
|
if (evt->status) {
|
||||||
bt_conn_security_changed(conn, evt->status,
|
bt_conn_security_changed(conn, evt->status,
|
||||||
security_err_get(evt->status));
|
bt_security_err_get(evt->status));
|
||||||
bt_conn_unref(conn);
|
bt_conn_unref(conn);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -4197,7 +3811,7 @@ static void hci_encrypt_key_refresh_complete(struct net_buf *buf)
|
||||||
|
|
||||||
if (evt->status) {
|
if (evt->status) {
|
||||||
bt_conn_security_changed(conn, evt->status,
|
bt_conn_security_changed(conn, evt->status,
|
||||||
security_err_get(evt->status));
|
bt_security_err_get(evt->status));
|
||||||
bt_conn_unref(conn);
|
bt_conn_unref(conn);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -5379,23 +4993,24 @@ static const struct event_handler normal_events[] = {
|
||||||
sizeof(struct bt_hci_evt_conn_request)),
|
sizeof(struct bt_hci_evt_conn_request)),
|
||||||
EVENT_HANDLER(BT_HCI_EVT_CONN_COMPLETE, conn_complete,
|
EVENT_HANDLER(BT_HCI_EVT_CONN_COMPLETE, conn_complete,
|
||||||
sizeof(struct bt_hci_evt_conn_complete)),
|
sizeof(struct bt_hci_evt_conn_complete)),
|
||||||
EVENT_HANDLER(BT_HCI_EVT_PIN_CODE_REQ, pin_code_req,
|
EVENT_HANDLER(BT_HCI_EVT_PIN_CODE_REQ, hci_evt_pin_code_req,
|
||||||
sizeof(struct bt_hci_evt_pin_code_req)),
|
sizeof(struct bt_hci_evt_pin_code_req)),
|
||||||
EVENT_HANDLER(BT_HCI_EVT_LINK_KEY_NOTIFY, link_key_notify,
|
EVENT_HANDLER(BT_HCI_EVT_LINK_KEY_NOTIFY, hci_evt_link_key_notify,
|
||||||
sizeof(struct bt_hci_evt_link_key_notify)),
|
sizeof(struct bt_hci_evt_link_key_notify)),
|
||||||
EVENT_HANDLER(BT_HCI_EVT_LINK_KEY_REQ, link_key_req,
|
EVENT_HANDLER(BT_HCI_EVT_LINK_KEY_REQ, hci_evt_link_key_req,
|
||||||
sizeof(struct bt_hci_evt_link_key_req)),
|
sizeof(struct bt_hci_evt_link_key_req)),
|
||||||
EVENT_HANDLER(BT_HCI_EVT_IO_CAPA_RESP, io_capa_resp,
|
EVENT_HANDLER(BT_HCI_EVT_IO_CAPA_RESP, hci_evt_io_capa_resp,
|
||||||
sizeof(struct bt_hci_evt_io_capa_resp)),
|
sizeof(struct bt_hci_evt_io_capa_resp)),
|
||||||
EVENT_HANDLER(BT_HCI_EVT_IO_CAPA_REQ, io_capa_req,
|
EVENT_HANDLER(BT_HCI_EVT_IO_CAPA_REQ, hci_evt_io_capa_req,
|
||||||
sizeof(struct bt_hci_evt_io_capa_req)),
|
sizeof(struct bt_hci_evt_io_capa_req)),
|
||||||
EVENT_HANDLER(BT_HCI_EVT_SSP_COMPLETE, ssp_complete,
|
EVENT_HANDLER(BT_HCI_EVT_SSP_COMPLETE, hci_evt_ssp_complete,
|
||||||
sizeof(struct bt_hci_evt_ssp_complete)),
|
sizeof(struct bt_hci_evt_ssp_complete)),
|
||||||
EVENT_HANDLER(BT_HCI_EVT_USER_CONFIRM_REQ, user_confirm_req,
|
EVENT_HANDLER(BT_HCI_EVT_USER_CONFIRM_REQ, hci_evt_user_confirm_req,
|
||||||
sizeof(struct bt_hci_evt_user_confirm_req)),
|
sizeof(struct bt_hci_evt_user_confirm_req)),
|
||||||
EVENT_HANDLER(BT_HCI_EVT_USER_PASSKEY_NOTIFY, user_passkey_notify,
|
EVENT_HANDLER(BT_HCI_EVT_USER_PASSKEY_NOTIFY,
|
||||||
|
hci_evt_user_passkey_notify,
|
||||||
sizeof(struct bt_hci_evt_user_passkey_notify)),
|
sizeof(struct bt_hci_evt_user_passkey_notify)),
|
||||||
EVENT_HANDLER(BT_HCI_EVT_USER_PASSKEY_REQ, user_passkey_req,
|
EVENT_HANDLER(BT_HCI_EVT_USER_PASSKEY_REQ, hci_evt_user_passkey_req,
|
||||||
sizeof(struct bt_hci_evt_user_passkey_req)),
|
sizeof(struct bt_hci_evt_user_passkey_req)),
|
||||||
EVENT_HANDLER(BT_HCI_EVT_INQUIRY_COMPLETE, inquiry_complete,
|
EVENT_HANDLER(BT_HCI_EVT_INQUIRY_COMPLETE, inquiry_complete,
|
||||||
sizeof(struct bt_hci_evt_inquiry_complete)),
|
sizeof(struct bt_hci_evt_inquiry_complete)),
|
||||||
|
@ -5408,7 +5023,7 @@ static const struct event_handler normal_events[] = {
|
||||||
EVENT_HANDLER(BT_HCI_EVT_REMOTE_NAME_REQ_COMPLETE,
|
EVENT_HANDLER(BT_HCI_EVT_REMOTE_NAME_REQ_COMPLETE,
|
||||||
remote_name_request_complete,
|
remote_name_request_complete,
|
||||||
sizeof(struct bt_hci_evt_remote_name_req_complete)),
|
sizeof(struct bt_hci_evt_remote_name_req_complete)),
|
||||||
EVENT_HANDLER(BT_HCI_EVT_AUTH_COMPLETE, auth_complete,
|
EVENT_HANDLER(BT_HCI_EVT_AUTH_COMPLETE, hci_evt_auth_complete,
|
||||||
sizeof(struct bt_hci_evt_auth_complete)),
|
sizeof(struct bt_hci_evt_auth_complete)),
|
||||||
EVENT_HANDLER(BT_HCI_EVT_REMOTE_FEATURES,
|
EVENT_HANDLER(BT_HCI_EVT_REMOTE_FEATURES,
|
||||||
read_remote_features_complete,
|
read_remote_features_complete,
|
||||||
|
|
|
@ -295,6 +295,8 @@ struct bt_dev {
|
||||||
extern struct bt_dev bt_dev;
|
extern struct bt_dev bt_dev;
|
||||||
#if defined(CONFIG_BT_SMP) || defined(CONFIG_BT_BREDR)
|
#if defined(CONFIG_BT_SMP) || defined(CONFIG_BT_BREDR)
|
||||||
extern const struct bt_conn_auth_cb *bt_auth;
|
extern const struct bt_conn_auth_cb *bt_auth;
|
||||||
|
|
||||||
|
enum bt_security_err bt_security_err_get(uint8_t hci_err);
|
||||||
#endif /* CONFIG_BT_SMP || CONFIG_BT_BREDR */
|
#endif /* CONFIG_BT_SMP || CONFIG_BT_BREDR */
|
||||||
|
|
||||||
int bt_hci_disconnect(uint16_t handle, uint8_t reason);
|
int bt_hci_disconnect(uint16_t handle, uint8_t reason);
|
||||||
|
@ -331,3 +333,15 @@ int bt_le_adv_start_internal(const struct bt_le_adv_param *param,
|
||||||
|
|
||||||
void bt_le_adv_resume(void);
|
void bt_le_adv_resume(void);
|
||||||
bool bt_le_scan_random_addr_check(void);
|
bool bt_le_scan_random_addr_check(void);
|
||||||
|
|
||||||
|
/* HCI event handlers */
|
||||||
|
void hci_evt_pin_code_req(struct net_buf *buf);
|
||||||
|
void hci_evt_link_key_notify(struct net_buf *buf);
|
||||||
|
void hci_evt_link_key_req(struct net_buf *buf);
|
||||||
|
void hci_evt_io_capa_resp(struct net_buf *buf);
|
||||||
|
void hci_evt_io_capa_req(struct net_buf *buf);
|
||||||
|
void hci_evt_ssp_complete(struct net_buf *buf);
|
||||||
|
void hci_evt_user_confirm_req(struct net_buf *buf);
|
||||||
|
void hci_evt_user_passkey_notify(struct net_buf *buf);
|
||||||
|
void hci_evt_user_passkey_req(struct net_buf *buf);
|
||||||
|
void hci_evt_auth_complete(struct net_buf *buf);
|
||||||
|
|
789
subsys/bluetooth/host/ssp.c
Normal file
789
subsys/bluetooth/host/ssp.c
Normal file
|
@ -0,0 +1,789 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2017 Nordic Semiconductor ASA
|
||||||
|
* Copyright (c) 2015 Intel Corporation
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <sys/byteorder.h>
|
||||||
|
|
||||||
|
#include <bluetooth/hci.h>
|
||||||
|
#include <bluetooth/addr.h>
|
||||||
|
|
||||||
|
#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_HCI_CORE)
|
||||||
|
#define LOG_MODULE_NAME bt_ssp
|
||||||
|
#include "common/log.h"
|
||||||
|
|
||||||
|
#include "keys.h"
|
||||||
|
|
||||||
|
#include "hci_core.h"
|
||||||
|
#include "conn_internal.h"
|
||||||
|
|
||||||
|
enum pairing_method {
|
||||||
|
LEGACY, /* Legacy (pre-SSP) pairing */
|
||||||
|
JUST_WORKS, /* JustWorks pairing */
|
||||||
|
PASSKEY_INPUT, /* Passkey Entry input */
|
||||||
|
PASSKEY_DISPLAY, /* Passkey Entry display */
|
||||||
|
PASSKEY_CONFIRM, /* Passkey confirm */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* based on table 5.7, Core Spec 4.2, Vol.3 Part C, 5.2.2.6 */
|
||||||
|
static const uint8_t ssp_method[4 /* remote */][4 /* local */] = {
|
||||||
|
{ JUST_WORKS, JUST_WORKS, PASSKEY_INPUT, JUST_WORKS },
|
||||||
|
{ JUST_WORKS, PASSKEY_CONFIRM, PASSKEY_INPUT, JUST_WORKS },
|
||||||
|
{ PASSKEY_DISPLAY, PASSKEY_DISPLAY, PASSKEY_INPUT, JUST_WORKS },
|
||||||
|
{ JUST_WORKS, JUST_WORKS, JUST_WORKS, JUST_WORKS },
|
||||||
|
};
|
||||||
|
|
||||||
|
static int pin_code_neg_reply(const bt_addr_t *bdaddr)
|
||||||
|
{
|
||||||
|
struct bt_hci_cp_pin_code_neg_reply *cp;
|
||||||
|
struct net_buf *buf;
|
||||||
|
|
||||||
|
BT_DBG("");
|
||||||
|
|
||||||
|
buf = bt_hci_cmd_create(BT_HCI_OP_PIN_CODE_NEG_REPLY, sizeof(*cp));
|
||||||
|
if (!buf) {
|
||||||
|
return -ENOBUFS;
|
||||||
|
}
|
||||||
|
|
||||||
|
cp = net_buf_add(buf, sizeof(*cp));
|
||||||
|
bt_addr_copy(&cp->bdaddr, bdaddr);
|
||||||
|
|
||||||
|
return bt_hci_cmd_send_sync(BT_HCI_OP_PIN_CODE_NEG_REPLY, buf, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pin_code_reply(struct bt_conn *conn, const char *pin, uint8_t len)
|
||||||
|
{
|
||||||
|
struct bt_hci_cp_pin_code_reply *cp;
|
||||||
|
struct net_buf *buf;
|
||||||
|
|
||||||
|
BT_DBG("");
|
||||||
|
|
||||||
|
buf = bt_hci_cmd_create(BT_HCI_OP_PIN_CODE_REPLY, sizeof(*cp));
|
||||||
|
if (!buf) {
|
||||||
|
return -ENOBUFS;
|
||||||
|
}
|
||||||
|
|
||||||
|
cp = net_buf_add(buf, sizeof(*cp));
|
||||||
|
|
||||||
|
bt_addr_copy(&cp->bdaddr, &conn->br.dst);
|
||||||
|
cp->pin_len = len;
|
||||||
|
strncpy((char *)cp->pin_code, pin, sizeof(cp->pin_code));
|
||||||
|
|
||||||
|
return bt_hci_cmd_send_sync(BT_HCI_OP_PIN_CODE_REPLY, buf, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
int bt_conn_auth_pincode_entry(struct bt_conn *conn, const char *pin)
|
||||||
|
{
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
if (!bt_auth) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (conn->type != BT_CONN_TYPE_BR) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
len = strlen(pin);
|
||||||
|
if (len > 16) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (conn->required_sec_level == BT_SECURITY_L3 && len < 16) {
|
||||||
|
BT_WARN("PIN code for %s is not 16 bytes wide",
|
||||||
|
bt_addr_str(&conn->br.dst));
|
||||||
|
return -EPERM;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Allow user send entered PIN to remote, then reset user state. */
|
||||||
|
if (!atomic_test_and_clear_bit(conn->flags, BT_CONN_USER)) {
|
||||||
|
return -EPERM;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len == 16) {
|
||||||
|
atomic_set_bit(conn->flags, BT_CONN_BR_LEGACY_SECURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
return pin_code_reply(conn, pin, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pin_code_req(struct bt_conn *conn)
|
||||||
|
{
|
||||||
|
if (bt_auth && bt_auth->pincode_entry) {
|
||||||
|
bool secure = false;
|
||||||
|
|
||||||
|
if (conn->required_sec_level == BT_SECURITY_L3) {
|
||||||
|
secure = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
atomic_set_bit(conn->flags, BT_CONN_USER);
|
||||||
|
atomic_set_bit(conn->flags, BT_CONN_BR_PAIRING);
|
||||||
|
bt_auth->pincode_entry(conn, secure);
|
||||||
|
} else {
|
||||||
|
pin_code_neg_reply(&conn->br.dst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t get_io_capa(void)
|
||||||
|
{
|
||||||
|
if (!bt_auth) {
|
||||||
|
return BT_IO_NO_INPUT_OUTPUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bt_auth->passkey_confirm && bt_auth->passkey_display) {
|
||||||
|
return BT_IO_DISPLAY_YESNO;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bt_auth->passkey_entry) {
|
||||||
|
return BT_IO_KEYBOARD_ONLY;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bt_auth->passkey_display) {
|
||||||
|
return BT_IO_DISPLAY_ONLY;
|
||||||
|
}
|
||||||
|
|
||||||
|
return BT_IO_NO_INPUT_OUTPUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t ssp_pair_method(const struct bt_conn *conn)
|
||||||
|
{
|
||||||
|
return ssp_method[conn->br.remote_io_capa][get_io_capa()];
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t ssp_get_auth(const struct bt_conn *conn)
|
||||||
|
{
|
||||||
|
/* Validate no bond auth request, and if valid use it. */
|
||||||
|
if ((conn->br.remote_auth == BT_HCI_NO_BONDING) ||
|
||||||
|
((conn->br.remote_auth == BT_HCI_NO_BONDING_MITM) &&
|
||||||
|
(ssp_pair_method(conn) > JUST_WORKS))) {
|
||||||
|
return conn->br.remote_auth;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Local & remote have enough IO capabilities to get MITM protection. */
|
||||||
|
if (ssp_pair_method(conn) > JUST_WORKS) {
|
||||||
|
return conn->br.remote_auth | BT_MITM;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* No MITM protection possible so ignore remote MITM requirement. */
|
||||||
|
return (conn->br.remote_auth & ~BT_MITM);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ssp_confirm_reply(struct bt_conn *conn)
|
||||||
|
{
|
||||||
|
struct bt_hci_cp_user_confirm_reply *cp;
|
||||||
|
struct net_buf *buf;
|
||||||
|
|
||||||
|
BT_DBG("");
|
||||||
|
|
||||||
|
buf = bt_hci_cmd_create(BT_HCI_OP_USER_CONFIRM_REPLY, sizeof(*cp));
|
||||||
|
if (!buf) {
|
||||||
|
return -ENOBUFS;
|
||||||
|
}
|
||||||
|
|
||||||
|
cp = net_buf_add(buf, sizeof(*cp));
|
||||||
|
bt_addr_copy(&cp->bdaddr, &conn->br.dst);
|
||||||
|
|
||||||
|
return bt_hci_cmd_send_sync(BT_HCI_OP_USER_CONFIRM_REPLY, buf, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ssp_confirm_neg_reply(struct bt_conn *conn)
|
||||||
|
{
|
||||||
|
struct bt_hci_cp_user_confirm_reply *cp;
|
||||||
|
struct net_buf *buf;
|
||||||
|
|
||||||
|
BT_DBG("");
|
||||||
|
|
||||||
|
buf = bt_hci_cmd_create(BT_HCI_OP_USER_CONFIRM_NEG_REPLY, sizeof(*cp));
|
||||||
|
if (!buf) {
|
||||||
|
return -ENOBUFS;
|
||||||
|
}
|
||||||
|
|
||||||
|
cp = net_buf_add(buf, sizeof(*cp));
|
||||||
|
bt_addr_copy(&cp->bdaddr, &conn->br.dst);
|
||||||
|
|
||||||
|
return bt_hci_cmd_send_sync(BT_HCI_OP_USER_CONFIRM_NEG_REPLY, buf,
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ssp_pairing_complete(struct bt_conn *conn, uint8_t status)
|
||||||
|
{
|
||||||
|
if (!status) {
|
||||||
|
bool bond = !atomic_test_bit(conn->flags, BT_CONN_BR_NOBOND);
|
||||||
|
|
||||||
|
if (bt_auth && bt_auth->pairing_complete) {
|
||||||
|
bt_auth->pairing_complete(conn, bond);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (bt_auth && bt_auth->pairing_failed) {
|
||||||
|
bt_auth->pairing_failed(conn, status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ssp_auth(struct bt_conn *conn, uint32_t passkey)
|
||||||
|
{
|
||||||
|
conn->br.pairing_method = ssp_pair_method(conn);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If local required security is HIGH then MITM is mandatory.
|
||||||
|
* MITM protection is no achievable when SSP 'justworks' is applied.
|
||||||
|
*/
|
||||||
|
if (conn->required_sec_level > BT_SECURITY_L2 &&
|
||||||
|
conn->br.pairing_method == JUST_WORKS) {
|
||||||
|
BT_DBG("MITM protection infeasible for required security");
|
||||||
|
ssp_confirm_neg_reply(conn);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (conn->br.pairing_method) {
|
||||||
|
case PASSKEY_CONFIRM:
|
||||||
|
atomic_set_bit(conn->flags, BT_CONN_USER);
|
||||||
|
bt_auth->passkey_confirm(conn, passkey);
|
||||||
|
break;
|
||||||
|
case PASSKEY_DISPLAY:
|
||||||
|
atomic_set_bit(conn->flags, BT_CONN_USER);
|
||||||
|
bt_auth->passkey_display(conn, passkey);
|
||||||
|
break;
|
||||||
|
case PASSKEY_INPUT:
|
||||||
|
atomic_set_bit(conn->flags, BT_CONN_USER);
|
||||||
|
bt_auth->passkey_entry(conn);
|
||||||
|
break;
|
||||||
|
case JUST_WORKS:
|
||||||
|
/*
|
||||||
|
* When local host works as pairing acceptor and 'justworks'
|
||||||
|
* model is applied then notify user about such pairing request.
|
||||||
|
* [BT Core 4.2 table 5.7, Vol 3, Part C, 5.2.2.6]
|
||||||
|
*/
|
||||||
|
if (bt_auth && bt_auth->pairing_confirm &&
|
||||||
|
!atomic_test_bit(conn->flags,
|
||||||
|
BT_CONN_BR_PAIRING_INITIATOR)) {
|
||||||
|
atomic_set_bit(conn->flags, BT_CONN_USER);
|
||||||
|
bt_auth->pairing_confirm(conn);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ssp_confirm_reply(conn);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ssp_passkey_reply(struct bt_conn *conn, unsigned int passkey)
|
||||||
|
{
|
||||||
|
struct bt_hci_cp_user_passkey_reply *cp;
|
||||||
|
struct net_buf *buf;
|
||||||
|
|
||||||
|
BT_DBG("");
|
||||||
|
|
||||||
|
buf = bt_hci_cmd_create(BT_HCI_OP_USER_PASSKEY_REPLY, sizeof(*cp));
|
||||||
|
if (!buf) {
|
||||||
|
return -ENOBUFS;
|
||||||
|
}
|
||||||
|
|
||||||
|
cp = net_buf_add(buf, sizeof(*cp));
|
||||||
|
bt_addr_copy(&cp->bdaddr, &conn->br.dst);
|
||||||
|
cp->passkey = sys_cpu_to_le32(passkey);
|
||||||
|
|
||||||
|
return bt_hci_cmd_send_sync(BT_HCI_OP_USER_PASSKEY_REPLY, buf, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ssp_passkey_neg_reply(struct bt_conn *conn)
|
||||||
|
{
|
||||||
|
struct bt_hci_cp_user_passkey_neg_reply *cp;
|
||||||
|
struct net_buf *buf;
|
||||||
|
|
||||||
|
BT_DBG("");
|
||||||
|
|
||||||
|
buf = bt_hci_cmd_create(BT_HCI_OP_USER_PASSKEY_NEG_REPLY, sizeof(*cp));
|
||||||
|
if (!buf) {
|
||||||
|
return -ENOBUFS;
|
||||||
|
}
|
||||||
|
|
||||||
|
cp = net_buf_add(buf, sizeof(*cp));
|
||||||
|
bt_addr_copy(&cp->bdaddr, &conn->br.dst);
|
||||||
|
|
||||||
|
return bt_hci_cmd_send_sync(BT_HCI_OP_USER_PASSKEY_NEG_REPLY, buf,
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int conn_auth(struct bt_conn *conn)
|
||||||
|
{
|
||||||
|
struct bt_hci_cp_auth_requested *auth;
|
||||||
|
struct net_buf *buf;
|
||||||
|
|
||||||
|
BT_DBG("");
|
||||||
|
|
||||||
|
buf = bt_hci_cmd_create(BT_HCI_OP_AUTH_REQUESTED, sizeof(*auth));
|
||||||
|
if (!buf) {
|
||||||
|
return -ENOBUFS;
|
||||||
|
}
|
||||||
|
|
||||||
|
auth = net_buf_add(buf, sizeof(*auth));
|
||||||
|
auth->handle = sys_cpu_to_le16(conn->handle);
|
||||||
|
|
||||||
|
atomic_set_bit(conn->flags, BT_CONN_BR_PAIRING_INITIATOR);
|
||||||
|
|
||||||
|
return bt_hci_cmd_send_sync(BT_HCI_OP_AUTH_REQUESTED, buf, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
int bt_ssp_start_security(struct bt_conn *conn)
|
||||||
|
{
|
||||||
|
if (atomic_test_bit(conn->flags, BT_CONN_BR_PAIRING)) {
|
||||||
|
return -EBUSY;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (conn->required_sec_level > BT_SECURITY_L3) {
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (get_io_capa() == BT_IO_NO_INPUT_OUTPUT &&
|
||||||
|
conn->required_sec_level > BT_SECURITY_L2) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return conn_auth(conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
int bt_ssp_auth_passkey_entry(struct bt_conn *conn, unsigned int passkey)
|
||||||
|
{
|
||||||
|
/* User entered passkey, reset user state. */
|
||||||
|
if (!atomic_test_and_clear_bit(conn->flags, BT_CONN_USER)) {
|
||||||
|
return -EPERM;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (conn->br.pairing_method == PASSKEY_INPUT) {
|
||||||
|
return ssp_passkey_reply(conn, passkey);
|
||||||
|
}
|
||||||
|
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bt_ssp_auth_passkey_confirm(struct bt_conn *conn)
|
||||||
|
{
|
||||||
|
/* Allow user confirm passkey value, then reset user state. */
|
||||||
|
if (!atomic_test_and_clear_bit(conn->flags, BT_CONN_USER)) {
|
||||||
|
return -EPERM;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ssp_confirm_reply(conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
int bt_ssp_auth_pairing_confirm(struct bt_conn *conn)
|
||||||
|
{
|
||||||
|
return ssp_confirm_reply(conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
int bt_ssp_auth_cancel(struct bt_conn *conn)
|
||||||
|
{
|
||||||
|
/* Allow user cancel authentication, then reset user state. */
|
||||||
|
if (!atomic_test_and_clear_bit(conn->flags, BT_CONN_USER)) {
|
||||||
|
return -EPERM;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (conn->br.pairing_method) {
|
||||||
|
case JUST_WORKS:
|
||||||
|
case PASSKEY_CONFIRM:
|
||||||
|
return ssp_confirm_neg_reply(conn);
|
||||||
|
case PASSKEY_INPUT:
|
||||||
|
return ssp_passkey_neg_reply(conn);
|
||||||
|
case PASSKEY_DISPLAY:
|
||||||
|
return bt_conn_disconnect(conn,
|
||||||
|
BT_HCI_ERR_AUTH_FAIL);
|
||||||
|
case LEGACY:
|
||||||
|
return pin_code_neg_reply(&conn->br.dst);
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void hci_evt_pin_code_req(struct net_buf *buf)
|
||||||
|
{
|
||||||
|
struct bt_hci_evt_pin_code_req *evt = (void *)buf->data;
|
||||||
|
struct bt_conn *conn;
|
||||||
|
|
||||||
|
BT_DBG("");
|
||||||
|
|
||||||
|
conn = bt_conn_lookup_addr_br(&evt->bdaddr);
|
||||||
|
if (!conn) {
|
||||||
|
BT_ERR("Can't find conn for %s", bt_addr_str(&evt->bdaddr));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pin_code_req(conn);
|
||||||
|
bt_conn_unref(conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
void hci_evt_link_key_notify(struct net_buf *buf)
|
||||||
|
{
|
||||||
|
struct bt_hci_evt_link_key_notify *evt = (void *)buf->data;
|
||||||
|
struct bt_conn *conn;
|
||||||
|
|
||||||
|
conn = bt_conn_lookup_addr_br(&evt->bdaddr);
|
||||||
|
if (!conn) {
|
||||||
|
BT_ERR("Can't find conn for %s", bt_addr_str(&evt->bdaddr));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
BT_DBG("%s, link type 0x%02x", bt_addr_str(&evt->bdaddr), evt->key_type);
|
||||||
|
|
||||||
|
if (!conn->br.link_key) {
|
||||||
|
conn->br.link_key = bt_keys_get_link_key(&evt->bdaddr);
|
||||||
|
}
|
||||||
|
if (!conn->br.link_key) {
|
||||||
|
BT_ERR("Can't update keys for %s", bt_addr_str(&evt->bdaddr));
|
||||||
|
bt_conn_unref(conn);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* clear any old Link Key flags */
|
||||||
|
conn->br.link_key->flags = 0U;
|
||||||
|
|
||||||
|
switch (evt->key_type) {
|
||||||
|
case BT_LK_COMBINATION:
|
||||||
|
/*
|
||||||
|
* Setting Combination Link Key as AUTHENTICATED means it was
|
||||||
|
* successfully generated by 16 digits wide PIN code.
|
||||||
|
*/
|
||||||
|
if (atomic_test_and_clear_bit(conn->flags,
|
||||||
|
BT_CONN_BR_LEGACY_SECURE)) {
|
||||||
|
conn->br.link_key->flags |= BT_LINK_KEY_AUTHENTICATED;
|
||||||
|
}
|
||||||
|
memcpy(conn->br.link_key->val, evt->link_key, 16);
|
||||||
|
break;
|
||||||
|
case BT_LK_AUTH_COMBINATION_P192:
|
||||||
|
conn->br.link_key->flags |= BT_LINK_KEY_AUTHENTICATED;
|
||||||
|
/* fall through */
|
||||||
|
case BT_LK_UNAUTH_COMBINATION_P192:
|
||||||
|
/* Mark no-bond so that link-key is removed on disconnection */
|
||||||
|
if (ssp_get_auth(conn) < BT_HCI_DEDICATED_BONDING) {
|
||||||
|
atomic_set_bit(conn->flags, BT_CONN_BR_NOBOND);
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(conn->br.link_key->val, evt->link_key, 16);
|
||||||
|
break;
|
||||||
|
case BT_LK_AUTH_COMBINATION_P256:
|
||||||
|
conn->br.link_key->flags |= BT_LINK_KEY_AUTHENTICATED;
|
||||||
|
/* fall through */
|
||||||
|
case BT_LK_UNAUTH_COMBINATION_P256:
|
||||||
|
conn->br.link_key->flags |= BT_LINK_KEY_SC;
|
||||||
|
|
||||||
|
/* Mark no-bond so that link-key is removed on disconnection */
|
||||||
|
if (ssp_get_auth(conn) < BT_HCI_DEDICATED_BONDING) {
|
||||||
|
atomic_set_bit(conn->flags, BT_CONN_BR_NOBOND);
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(conn->br.link_key->val, evt->link_key, 16);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
BT_WARN("Unsupported Link Key type %u", evt->key_type);
|
||||||
|
(void)memset(conn->br.link_key->val, 0,
|
||||||
|
sizeof(conn->br.link_key->val));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IS_ENABLED(CONFIG_BT_SETTINGS) &&
|
||||||
|
!atomic_test_bit(conn->flags, BT_CONN_BR_NOBOND)) {
|
||||||
|
bt_keys_link_key_store(conn->br.link_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
bt_conn_unref(conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
void link_key_neg_reply(const bt_addr_t *bdaddr)
|
||||||
|
{
|
||||||
|
struct bt_hci_cp_link_key_neg_reply *cp;
|
||||||
|
struct net_buf *buf;
|
||||||
|
|
||||||
|
BT_DBG("");
|
||||||
|
|
||||||
|
buf = bt_hci_cmd_create(BT_HCI_OP_LINK_KEY_NEG_REPLY, sizeof(*cp));
|
||||||
|
if (!buf) {
|
||||||
|
BT_ERR("Out of command buffers");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cp = net_buf_add(buf, sizeof(*cp));
|
||||||
|
bt_addr_copy(&cp->bdaddr, bdaddr);
|
||||||
|
bt_hci_cmd_send_sync(BT_HCI_OP_LINK_KEY_NEG_REPLY, buf, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void link_key_reply(const bt_addr_t *bdaddr, const uint8_t *lk)
|
||||||
|
{
|
||||||
|
struct bt_hci_cp_link_key_reply *cp;
|
||||||
|
struct net_buf *buf;
|
||||||
|
|
||||||
|
BT_DBG("");
|
||||||
|
|
||||||
|
buf = bt_hci_cmd_create(BT_HCI_OP_LINK_KEY_REPLY, sizeof(*cp));
|
||||||
|
if (!buf) {
|
||||||
|
BT_ERR("Out of command buffers");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cp = net_buf_add(buf, sizeof(*cp));
|
||||||
|
bt_addr_copy(&cp->bdaddr, bdaddr);
|
||||||
|
memcpy(cp->link_key, lk, 16);
|
||||||
|
bt_hci_cmd_send_sync(BT_HCI_OP_LINK_KEY_REPLY, buf, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void hci_evt_link_key_req(struct net_buf *buf)
|
||||||
|
{
|
||||||
|
struct bt_hci_evt_link_key_req *evt = (void *)buf->data;
|
||||||
|
struct bt_conn *conn;
|
||||||
|
|
||||||
|
BT_DBG("%s", bt_addr_str(&evt->bdaddr));
|
||||||
|
|
||||||
|
conn = bt_conn_lookup_addr_br(&evt->bdaddr);
|
||||||
|
if (!conn) {
|
||||||
|
BT_ERR("Can't find conn for %s", bt_addr_str(&evt->bdaddr));
|
||||||
|
link_key_neg_reply(&evt->bdaddr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!conn->br.link_key) {
|
||||||
|
conn->br.link_key = bt_keys_find_link_key(&evt->bdaddr);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!conn->br.link_key) {
|
||||||
|
link_key_neg_reply(&evt->bdaddr);
|
||||||
|
bt_conn_unref(conn);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Enforce regenerate by controller stronger link key since found one
|
||||||
|
* in database not covers requested security level.
|
||||||
|
*/
|
||||||
|
if (!(conn->br.link_key->flags & BT_LINK_KEY_AUTHENTICATED) &&
|
||||||
|
conn->required_sec_level > BT_SECURITY_L2) {
|
||||||
|
link_key_neg_reply(&evt->bdaddr);
|
||||||
|
bt_conn_unref(conn);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
link_key_reply(&evt->bdaddr, conn->br.link_key->val);
|
||||||
|
bt_conn_unref(conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
void io_capa_neg_reply(const bt_addr_t *bdaddr, const uint8_t reason)
|
||||||
|
{
|
||||||
|
struct bt_hci_cp_io_capability_neg_reply *cp;
|
||||||
|
struct net_buf *resp_buf;
|
||||||
|
|
||||||
|
resp_buf = bt_hci_cmd_create(BT_HCI_OP_IO_CAPABILITY_NEG_REPLY,
|
||||||
|
sizeof(*cp));
|
||||||
|
if (!resp_buf) {
|
||||||
|
BT_ERR("Out of command buffers");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cp = net_buf_add(resp_buf, sizeof(*cp));
|
||||||
|
bt_addr_copy(&cp->bdaddr, bdaddr);
|
||||||
|
cp->reason = reason;
|
||||||
|
bt_hci_cmd_send_sync(BT_HCI_OP_IO_CAPABILITY_NEG_REPLY, resp_buf, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void hci_evt_io_capa_resp(struct net_buf *buf)
|
||||||
|
{
|
||||||
|
struct bt_hci_evt_io_capa_resp *evt = (void *)buf->data;
|
||||||
|
struct bt_conn *conn;
|
||||||
|
|
||||||
|
BT_DBG("remote %s, IOcapa 0x%02x, auth 0x%02x",
|
||||||
|
bt_addr_str(&evt->bdaddr), evt->capability, evt->authentication);
|
||||||
|
|
||||||
|
if (evt->authentication > BT_HCI_GENERAL_BONDING_MITM) {
|
||||||
|
BT_ERR("Invalid remote authentication requirements");
|
||||||
|
io_capa_neg_reply(&evt->bdaddr,
|
||||||
|
BT_HCI_ERR_UNSUPP_FEATURE_PARAM_VAL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (evt->capability > BT_IO_NO_INPUT_OUTPUT) {
|
||||||
|
BT_ERR("Invalid remote io capability requirements");
|
||||||
|
io_capa_neg_reply(&evt->bdaddr,
|
||||||
|
BT_HCI_ERR_UNSUPP_FEATURE_PARAM_VAL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
conn = bt_conn_lookup_addr_br(&evt->bdaddr);
|
||||||
|
if (!conn) {
|
||||||
|
BT_ERR("Unable to find conn for %s", bt_addr_str(&evt->bdaddr));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
conn->br.remote_io_capa = evt->capability;
|
||||||
|
conn->br.remote_auth = evt->authentication;
|
||||||
|
atomic_set_bit(conn->flags, BT_CONN_BR_PAIRING);
|
||||||
|
bt_conn_unref(conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
void hci_evt_io_capa_req(struct net_buf *buf)
|
||||||
|
{
|
||||||
|
struct bt_hci_evt_io_capa_req *evt = (void *)buf->data;
|
||||||
|
struct net_buf *resp_buf;
|
||||||
|
struct bt_conn *conn;
|
||||||
|
struct bt_hci_cp_io_capability_reply *cp;
|
||||||
|
uint8_t auth;
|
||||||
|
|
||||||
|
BT_DBG("");
|
||||||
|
|
||||||
|
conn = bt_conn_lookup_addr_br(&evt->bdaddr);
|
||||||
|
if (!conn) {
|
||||||
|
BT_ERR("Can't find conn for %s", bt_addr_str(&evt->bdaddr));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
resp_buf = bt_hci_cmd_create(BT_HCI_OP_IO_CAPABILITY_REPLY,
|
||||||
|
sizeof(*cp));
|
||||||
|
if (!resp_buf) {
|
||||||
|
BT_ERR("Out of command buffers");
|
||||||
|
bt_conn_unref(conn);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set authentication requirements when acting as pairing initiator to
|
||||||
|
* 'dedicated bond' with MITM protection set if local IO capa
|
||||||
|
* potentially allows it, and for acceptor, based on local IO capa and
|
||||||
|
* remote's authentication set.
|
||||||
|
*/
|
||||||
|
if (atomic_test_bit(conn->flags, BT_CONN_BR_PAIRING_INITIATOR)) {
|
||||||
|
if (get_io_capa() != BT_IO_NO_INPUT_OUTPUT) {
|
||||||
|
auth = BT_HCI_DEDICATED_BONDING_MITM;
|
||||||
|
} else {
|
||||||
|
auth = BT_HCI_DEDICATED_BONDING;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
auth = ssp_get_auth(conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
cp = net_buf_add(resp_buf, sizeof(*cp));
|
||||||
|
bt_addr_copy(&cp->bdaddr, &evt->bdaddr);
|
||||||
|
cp->capability = get_io_capa();
|
||||||
|
cp->authentication = auth;
|
||||||
|
cp->oob_data = 0U;
|
||||||
|
bt_hci_cmd_send_sync(BT_HCI_OP_IO_CAPABILITY_REPLY, resp_buf, NULL);
|
||||||
|
bt_conn_unref(conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
void hci_evt_ssp_complete(struct net_buf *buf)
|
||||||
|
{
|
||||||
|
struct bt_hci_evt_ssp_complete *evt = (void *)buf->data;
|
||||||
|
struct bt_conn *conn;
|
||||||
|
|
||||||
|
BT_DBG("status 0x%02x", evt->status);
|
||||||
|
|
||||||
|
conn = bt_conn_lookup_addr_br(&evt->bdaddr);
|
||||||
|
if (!conn) {
|
||||||
|
BT_ERR("Can't find conn for %s", bt_addr_str(&evt->bdaddr));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssp_pairing_complete(conn, bt_security_err_get(evt->status));
|
||||||
|
if (evt->status) {
|
||||||
|
bt_conn_disconnect(conn, BT_HCI_ERR_AUTH_FAIL);
|
||||||
|
}
|
||||||
|
|
||||||
|
bt_conn_unref(conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
void hci_evt_user_confirm_req(struct net_buf *buf)
|
||||||
|
{
|
||||||
|
struct bt_hci_evt_user_confirm_req *evt = (void *)buf->data;
|
||||||
|
struct bt_conn *conn;
|
||||||
|
|
||||||
|
conn = bt_conn_lookup_addr_br(&evt->bdaddr);
|
||||||
|
if (!conn) {
|
||||||
|
BT_ERR("Can't find conn for %s", bt_addr_str(&evt->bdaddr));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssp_auth(conn, sys_le32_to_cpu(evt->passkey));
|
||||||
|
bt_conn_unref(conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
void hci_evt_user_passkey_notify(struct net_buf *buf)
|
||||||
|
{
|
||||||
|
struct bt_hci_evt_user_passkey_notify *evt = (void *)buf->data;
|
||||||
|
struct bt_conn *conn;
|
||||||
|
|
||||||
|
BT_DBG("");
|
||||||
|
|
||||||
|
conn = bt_conn_lookup_addr_br(&evt->bdaddr);
|
||||||
|
if (!conn) {
|
||||||
|
BT_ERR("Can't find conn for %s", bt_addr_str(&evt->bdaddr));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssp_auth(conn, sys_le32_to_cpu(evt->passkey));
|
||||||
|
bt_conn_unref(conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
void hci_evt_user_passkey_req(struct net_buf *buf)
|
||||||
|
{
|
||||||
|
struct bt_hci_evt_user_passkey_req *evt = (void *)buf->data;
|
||||||
|
struct bt_conn *conn;
|
||||||
|
|
||||||
|
conn = bt_conn_lookup_addr_br(&evt->bdaddr);
|
||||||
|
if (!conn) {
|
||||||
|
BT_ERR("Can't find conn for %s", bt_addr_str(&evt->bdaddr));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssp_auth(conn, 0);
|
||||||
|
bt_conn_unref(conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void link_encr(const uint16_t handle)
|
||||||
|
{
|
||||||
|
struct bt_hci_cp_set_conn_encrypt *encr;
|
||||||
|
struct net_buf *buf;
|
||||||
|
|
||||||
|
BT_DBG("");
|
||||||
|
|
||||||
|
buf = bt_hci_cmd_create(BT_HCI_OP_SET_CONN_ENCRYPT, sizeof(*encr));
|
||||||
|
if (!buf) {
|
||||||
|
BT_ERR("Out of command buffers");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
encr = net_buf_add(buf, sizeof(*encr));
|
||||||
|
encr->handle = sys_cpu_to_le16(handle);
|
||||||
|
encr->encrypt = 0x01;
|
||||||
|
|
||||||
|
bt_hci_cmd_send_sync(BT_HCI_OP_SET_CONN_ENCRYPT, buf, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void hci_evt_auth_complete(struct net_buf *buf)
|
||||||
|
{
|
||||||
|
struct bt_hci_evt_auth_complete *evt = (void *)buf->data;
|
||||||
|
struct bt_conn *conn;
|
||||||
|
uint16_t handle = sys_le16_to_cpu(evt->handle);
|
||||||
|
|
||||||
|
BT_DBG("status 0x%02x, handle %u", evt->status, handle);
|
||||||
|
|
||||||
|
conn = bt_conn_lookup_handle(handle);
|
||||||
|
if (!conn) {
|
||||||
|
BT_ERR("Can't find conn for handle %u", handle);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (evt->status) {
|
||||||
|
/*
|
||||||
|
* Inform layers above HCI about non-zero authentication
|
||||||
|
* status to make them able cleanup pending jobs.
|
||||||
|
*/
|
||||||
|
bt_conn_security_changed(conn, evt->status,
|
||||||
|
bt_security_err_get(evt->status));
|
||||||
|
} else {
|
||||||
|
link_encr(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
bt_conn_unref(conn);
|
||||||
|
}
|
13
subsys/bluetooth/host/ssp.h
Normal file
13
subsys/bluetooth/host/ssp.h
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 Nordic Semiconductor ASA
|
||||||
|
* Copyright (c) 2017 Intel Corporation
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
int bt_ssp_start_security(struct bt_conn *conn);
|
||||||
|
|
||||||
|
int bt_ssp_auth_passkey_entry(struct bt_conn *conn, unsigned int passkey);
|
||||||
|
int bt_ssp_auth_passkey_confirm(struct bt_conn *conn);
|
||||||
|
int bt_ssp_auth_cancel(struct bt_conn *conn);
|
||||||
|
int bt_ssp_auth_pairing_confirm(struct bt_conn *conn);
|
Loading…
Add table
Add a link
Reference in a new issue