/**
* @file smp.c
* Security Manager Protocol implementation
*/
/*
* Copyright (c) 2015-2016 Intel Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <nanokernel.h>
#include <arch/cpu.h>
#include <stddef.h>
#include <errno.h>
#include <string.h>
#include <atomic.h>
#include <misc/util.h>
#include <misc/byteorder.h>
#include <misc/stack.h>
#include <misc/nano_work.h>
#include <net/buf.h>
#include <bluetooth/log.h>
#include <bluetooth/hci.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/conn.h>
#include <bluetooth/buf.h>
#include <tinycrypt/constants.h>
#include <tinycrypt/aes.h>
#include <tinycrypt/utils.h>
#include <tinycrypt/cmac_mode.h>
#include "hci_core.h"
#include "ecc.h"
#include "keys.h"
#include "conn_internal.h"
#include "l2cap_internal.h"
#include "smp.h"
#if !defined(CONFIG_BLUETOOTH_DEBUG_SMP)
#undef BT_DBG
#define BT_DBG(fmt, ...)
#endif
#define SMP_TIMEOUT (30 * sys_clock_ticks_per_sec)
#if defined(CONFIG_BLUETOOTH_SIGNING)
#define SIGN_DIST BT_SMP_DIST_SIGN
#else
#define SIGN_DIST 0
#endif
#define RECV_KEYS (BT_SMP_DIST_ID_KEY | BT_SMP_DIST_ENC_KEY | SIGN_DIST)
#if defined(CONFIG_BLUETOOTH_PRIVACY)
#define SEND_KEYS (BT_SMP_DIST_ENC_KEY | BT_SMP_DIST_ID_KEY | SIGN_DIST)
#else
#define SEND_KEYS (BT_SMP_DIST_ENC_KEY | SIGN_DIST)
#endif
#define RECV_KEYS_SC (RECV_KEYS & ~(BT_SMP_DIST_ENC_KEY | BT_SMP_DIST_LINK_KEY))
#define SEND_KEYS_SC (SEND_KEYS & ~(BT_SMP_DIST_ENC_KEY | BT_SMP_DIST_LINK_KEY))
#define BT_SMP_AUTH_MASK 0x07
#define BT_SMP_AUTH_MASK_SC 0x0f
enum pairing_method {
JUST_WORKS, /* JustWorks pairing */
PASSKEY_INPUT, /* Passkey Entry input */
PASSKEY_DISPLAY, /* Passkey Entry display */
PASSKEY_CONFIRM, /* Passkey confirm */
PASSKEY_ROLE, /* Passkey Entry depends on role */
};
enum {
SMP_FLAG_CFM_DELAYED, /* if confirm should be send when TK is valid */
SMP_FLAG_ENC_PENDING, /* if waiting for an encryption change event */
SMP_FLAG_KEYS_DISTR, /* if keys distribution phase is in progress */
SMP_FLAG_PAIRING, /* if pairing is in progress */
SMP_FLAG_TIMEOUT, /* if SMP timeout occurred */
SMP_FLAG_SC, /* if LE Secure Connections is used */
SMP_FLAG_PKEY_SEND, /* if should send Public Key when available */
SMP_FLAG_DHKEY_PENDING, /* if waiting for local DHKey */
SMP_FLAG_DHKEY_SEND, /* if should generate and send DHKey Check */
SMP_FLAG_USER, /* if waiting for user input */
SMP_FLAG_BOND, /* if bonding */
SMP_FLAG_SC_DEBUG_KEY, /* if Secure Connection are using debug key */
SMP_FLAG_SEC_REQ, /* if Security Request was sent/received */
SMP_FLAG_DHCHECK_WAIT, /* if waiting for remote DHCheck (as slave) */
/* Total number of flags - must be at the end */
SMP_NUM_FLAGS,
};
/* SMP channel specific context */
struct bt_smp {
/* The channel this context is associated with */
struct bt_l2cap_le_chan chan;
/* Commands that remote is allowed to send */
atomic_t allowed_cmds;
/* Flags for SMP state machine */
ATOMIC_DEFINE(flags, SMP_NUM_FLAGS);
/* Type of method used for pairing */
uint8_t method;
/* Pairing Request PDU */
uint8_t preq[7];
/* Pairing Response PDU */
uint8_t prsp[7];
/* Pairing Confirm PDU */
uint8_t pcnf[16];
/* Local random number */
uint8_t prnd[16];
/* Remote random number */
uint8_t rrnd[16];
/* Temporary key */
uint8_t tk[16];
/* Remote Public Key for LE SC */
uint8_t pkey[64];
/* DHKey */
uint8_t dhkey[32];
/* Remote DHKey check */
uint8_t e[16];
/* MacKey */
uint8_t mackey[16];
/* LE SC passkey */
uint32_t passkey;
/* LE SC passkey round */
uint8_t passkey_round;
/* Local key distribution */
uint8_t local_dist;
/* Remote key distribution */
uint8_t remote_dist;
/* Delayed work for timeout handling */
struct nano_delayed_work work;
};
/* based on table 2.8 Core Spec 2.3.5.1 Vol. 3 Part H */
static const uint8_t gen_method_legacy[5 /* remote */][5 /* local */] = {
{ JUST_WORKS, JUST_WORKS, PASSKEY_INPUT, JUST_WORKS, PASSKEY_INPUT },
{ JUST_WORKS, JUST_WORKS, PASSKEY_INPUT, JUST_WORKS, PASSKEY_INPUT },
{ PASSKEY_DISPLAY, PASSKEY_DISPLAY, PASSKEY_INPUT, JUST_WORKS,
PASSKEY_DISPLAY },
{ JUST_WORKS, JUST_WORKS, JUST_WORKS, JUST_WORKS, JUST_WORKS },
{ PASSKEY_DISPLAY, PASSKEY_DISPLAY, PASSKEY_INPUT, JUST_WORKS,
PASSKEY_ROLE },
};
/* based on table 2.8 Core Spec 2.3.5.1 Vol. 3 Part H */
static const uint8_t gen_method_sc[5 /* remote */][5 /* local */] = {
{ JUST_WORKS, JUST_WORKS, PASSKEY_INPUT, JUST_WORKS, PASSKEY_INPUT },
{ JUST_WORKS, PASSKEY_CONFIRM, PASSKEY_INPUT, JUST_WORKS,
PASSKEY_CONFIRM },
{ PASSKEY_DISPLAY, PASSKEY_DISPLAY, PASSKEY_INPUT, JUST_WORKS,
PASSKEY_DISPLAY },
{ JUST_WORKS, JUST_WORKS, JUST_WORKS, JUST_WORKS, JUST_WORKS },
{ PASSKEY_DISPLAY, PASSKEY_CONFIRM, PASSKEY_INPUT, JUST_WORKS,
PASSKEY_CONFIRM },
};
/* based on Core Specification 4.2 Vol 3. Part H 2.3.5.6.1 */
static const uint32_t sc_debug_private_key[8] = {
0xcd3c1abd, 0x5899b8a6, 0xeb40b799, 0x4aff607b, 0xd2103f50, 0x74c9b3e3,
0xa3c55f38, 0x3f49f6d4
};
static const uint8_t sc_debug_public_key[64] = {
0xe6, 0x9d, 0x35, 0x0e, 0x48, 0x01, 0x03, 0xcc, 0xdb, 0xfd, 0xf4, 0xac,
0x11, 0x91, 0xf4, 0xef, 0xb9, 0xa5, 0xf9, 0xe9, 0xa7, 0x83, 0x2c, 0x5e,
0x2c, 0xbe, 0x97, 0xf2, 0xd2, 0x03, 0xb0, 0x20, 0x8b, 0xd2, 0x89, 0x15,
0xd0, 0x8e, 0x1c, 0x74, 0x24, 0x30, 0xed, 0x8f, 0xc2, 0x45, 0x63, 0x76,
0x5c, 0x15, 0x52, 0x5a, 0xbf, 0x9a, 0x32, 0x63, 0x6d, 0xeb, 0x2a, 0x65,
0x49, 0x9c, 0x80, 0xdc
};
/* Pool for outgoing LE signaling packets, MTU is 65 */
static struct nano_fifo smp_buf;
static NET_BUF_POOL(smp_pool, CONFIG_BLUETOOTH_MAX_CONN,
BT_L2CAP_BUF_SIZE(65), &smp_buf, NULL,
BT_BUF_USER_DATA_MIN);
static struct bt_smp bt_smp_pool[CONFIG_BLUETOOTH_MAX_CONN];
static bool sc_supported;
static bool sc_local_pkey_valid;
static uint8_t sc_public_key[64];
static uint8_t get_io_capa(void)
{
if (!bt_auth) {
return BT_SMP_IO_NO_INPUT_OUTPUT;
}
/* Passkey Confirmation is valid only for LE SC */
if (bt_auth->passkey_display && bt_auth->passkey_entry &&
(bt_auth->passkey_confirm || !sc_supported)) {
return BT_SMP_IO_KEYBOARD_DISPLAY;
}
/* DisplayYesNo is useful only for LE SC */
if (sc_supported && bt_auth->passkey_display &&
bt_auth->passkey_confirm) {
return BT_SMP_IO_DISPLAY_YESNO;
}
if (bt_auth->passkey_entry) {
return BT_SMP_IO_KEYBOARD_ONLY;
}
if (bt_auth->passkey_display) {
return BT_SMP_IO_DISPLAY_ONLY;
}
return BT_SMP_IO_NO_INPUT_OUTPUT;
}
static uint8_t get_pair_method(struct bt_smp *smp, uint8_t remote_io)
{
struct bt_smp_pairing *req, *rsp;
if (remote_io > BT_SMP_IO_KEYBOARD_DISPLAY)
return JUST_WORKS;
req = (struct bt_smp_pairing *)&smp->preq[1];
rsp = (struct bt_smp_pairing *)&smp->prsp[1];
/* if none side requires MITM use JustWorks */
if (!((req->auth_req | rsp->auth_req) & BT_SMP_AUTH_MITM)) {
return JUST_WORKS;
}
return gen_method_sc[remote_io][get_io_capa()];
}
/* swap octets for LE encrypt */
static void swap_buf(uint8_t *dst, const uint8_t *src, uint16_t len)
{
int i;
for (i = 0; i < len; i++) {
dst[len - 1 - i] = src[i];
}
}
static void swap_in_place(uint8_t *buf, uint16_t len)
{
int i, j;
for (i = 0, j = len - 1; i < j; i++, j--) {
uint8_t tmp = buf[i];
buf[i] = buf[j];
buf[j] = tmp;
}
}
static int le_encrypt(const uint8_t key[16], const uint8_t plaintext[16],
uint8_t enc_data[16])
{
struct tc_aes_key_sched_struct s;
uint8_t tmp[16];
BT_DBG("key %s plaintext %s", bt_hex(key, 16), bt_hex(plaintext, 16));
swap_buf(tmp, key, 16);
if (tc_aes128_set_encrypt_key(&s, tmp) == TC_CRYPTO_FAIL) {
return -EINVAL;
}
swap_buf(tmp, plaintext, 16);
if (tc_aes_encrypt(enc_data, tmp, &s) == TC_CRYPTO_FAIL) {
return -EINVAL;
}
swap_in_place(enc_data, 16);
BT_DBG("enc_data %s", bt_hex(enc_data, 16));
return 0;
}
static int smp_ah(const uint8_t irk[16], const uint8_t r[3], uint8_t out[3])
{
uint8_t res[16];
int err;
BT_DBG("irk %s, r %s", bt_hex(irk, 16), bt_hex(r, 3));
/* r' = padding || r */
memcpy(res, r, 3);
memset(res + 3, 0, 13);
err = le_encrypt(irk, res, res);
if (err) {
return err;
}
/* The output of the random address function ah is:
* ah(h, r) = e(k, r') mod 2^24
* The output of the security function e is then truncated to 24 bits
* by taking the least significant 24 bits of the output of e as the
* result of ah.
*/
memcpy(out, res, 3);
return 0;
}
/* Cypher based Message Authentication Code (CMAC) with AES 128 bit
*
* Input : key ( 128-bit key )
* : in ( message to be authenticated )
* : len ( length of the message in octets )
* Output : out ( message authentication code )
*/
static int bt_smp_aes_cmac(const uint8_t *key, const uint8_t *in, size_t len,
uint8_t *out)
{
struct tc_aes_key_sched_struct sched;
struct tc_cmac_struct state;
if (tc_cmac_setup(&state, key, &sched) == TC_CRYPTO_FAIL) {
return -EIO;
}
if (tc_cmac_update(&state, in, len) == TC_CRYPTO_FAIL) {
return -EIO;
}
if (tc_cmac_final(out, &state) == TC_CRYPTO_FAIL) {
return -EIO;
}
return 0;
}
static int smp_f4(const uint8_t *u, const uint8_t *v, const uint8_t *x,
uint8_t z, uint8_t res[16])
{
uint8_t xs[16];
uint8_t m[65];
int err;
BT_DBG("u %s", bt_hex(u, 32));
BT_DBG("v %s", bt_hex(v, 32));
BT_DBG("x %s z 0x%x", bt_hex(x, 16), z);
/*
* U, V and Z are concatenated and used as input m to the function
* AES-CMAC and X is used as the key k.
*
* Core Spec 4.2 Vol 3 Part H 2.2.5
*
* note:
* bt_smp_aes_cmac uses BE data and smp_f4 accept LE so we swap
*/
swap_buf(m, u, 32);
swap_buf(m + 32, v, 32);
m[64] = z;
swap_buf(xs, x, 16);
err = bt_smp_aes_cmac(xs, m, sizeof(m), res);
if (err) {
return err;
}
swap_in_place(res, 16);
BT_DBG("res %s", bt_hex(res, 16));
return err;
}
static int smp_f5(const uint8_t *w, const uint8_t *n1, const uint8_t *n2,
const bt_addr_le_t *a1, const bt_addr_le_t *a2, uint8_t *mackey,
uint8_t *ltk)
{
static const uint8_t salt[16] = { 0x6c, 0x88, 0x83, 0x91, 0xaa, 0xf5,
0xa5, 0x38, 0x60, 0x37, 0x0b, 0xdb,
0x5a, 0x60, 0x83, 0xbe };
uint8_t m[53] = { 0x00, /* counter */
0x62, 0x74, 0x6c, 0x65, /* keyID */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*n1*/
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*2*/
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* a1 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* a2 */
0x01, 0x00 /* length */ };
uint8_t t[16], ws[32];
int err;
BT_DBG("w %s", bt_hex(w, 32));
BT_DBG("n1 %s n2 %s", bt_hex(n1, 16), bt_hex(n2, 16));
swap_buf(ws, w, 32);
err = bt_smp_aes_cmac(salt, ws, 32, t);
if (err) {
return err;
}
BT_DBG("t %s", bt_hex(t, 16));
swap_buf(m + 5, n1, 16);
swap_buf(m + 21, n2, 16);
m[37] = a1->type;
swap_buf(m + 38, a1->a.val, 6);
m[44] = a2->type;
swap_buf(m + 45, a2->a.val, 6);
err = bt_smp_aes_cmac(t, m, sizeof(m), mackey);
if (err) {
return err;
}
BT_DBG("mackey %1s", bt_hex(mackey, 16));
swap_in_place(mackey, 16);
/* counter for ltk is 1 */
m[0] = 0x01;
err = bt_smp_aes_cmac(t, m, sizeof(m), ltk);
if (err) {
return err;
}
BT_DBG("ltk %s", bt_hex(ltk, 16));
swap_in_place(ltk, 16);
return 0;
}
static int smp_f6(const uint8_t *w, const uint8_t *n1, const uint8_t *n2,
const uint8_t *r, const uint8_t *iocap, const bt_addr_le_t *a1,
const bt_addr_le_t *a2, uint8_t *check)
{
uint8_t ws[16];
uint8_t m[65];
int err;
BT_DBG("w %s", bt_hex(w, 16));
BT_DBG("n1 %s n2 %s", bt_hex(n1, 16), bt_hex(n2, 16));
BT_DBG("r %s io_cap %s", bt_hex(r, 16), bt_hex(iocap, 3));
BT_DBG("a1 %s a2 %s", bt_hex(a1, 7), bt_hex(a2, 7));
swap_buf(m, n1, 16);
swap_buf(m + 16, n2, 16);
swap_buf(m + 32, r, 16);
swap_buf(m + 48, iocap, 3);
m[51] = a1->type;
memcpy(m + 52, a1->a.val, 6);
swap_buf(m + 52, a1->a.val, 6);
m[58] = a2->type;
memcpy(m + 59, a2->a.val, 6);
swap_buf(m + 59, a2->a.val, 6);
swap_buf(ws, w, 16);
err = bt_smp_aes_cmac(ws, m, sizeof(m), check);
if (err) {
return err;
}
BT_DBG("res %s", bt_hex(check, 16));
swap_in_place(check, 16);
return 0;
}
static int smp_g2(const uint8_t u[32], const uint8_t v[32],
const uint8_t x[16], const uint8_t y[16], uint32_t *passkey)
{
uint8_t m[80], xs[16];
int err;
BT_DBG("u %s", bt_hex(u, 32));
BT_DBG("v %s", bt_hex(v, 32));
BT_DBG("x %s y %s", bt_hex(x, 16), bt_hex(y, 16));
swap_buf(m, u, 32);
swap_buf(m + 32, v, 32);
swap_buf(m + 64, y, 16);
swap_buf(xs, x, 16);
/* reuse xs (key) as buffer for result */
err = bt_smp_aes_cmac(xs, m, sizeof(m), xs);
if (err) {
return err;
}
BT_DBG("res %s", bt_hex(xs, 16));
memcpy(passkey, xs + 12, 4);
*passkey = sys_be32_to_cpu(*passkey) % 1000000;
BT_DBG("passkey %u", *passkey);
return 0;
}
static void smp_reset(struct bt_smp *smp)
{
struct bt_conn *conn = smp->chan.chan.conn;
nano_delayed_work_cancel(&smp->work);
smp->method = JUST_WORKS;
atomic_set(&smp->allowed_cmds, 0);
atomic_set(smp->flags, 0);
if (conn->required_sec_level != conn->sec_level) {
/* TODO report error */
/* reset required security level in case of error */
conn->required_sec_level = conn->sec_level;
}
#if defined(CONFIG_BLUETOOTH_CENTRAL)
if (conn->role == BT_HCI_ROLE_MASTER) {
atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_SECURITY_REQUEST);
return;
}
#endif /* CONFIG_BLUETOOTH_CENTRAL */
#if defined(CONFIG_BLUETOOTH_PERIPHERAL)
atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_PAIRING_REQ);
#endif /* CONFIG_BLUETOOTH_PERIPHERAL */
}
static void smp_timeout(struct nano_work *work)
{
struct bt_smp *smp = CONTAINER_OF(work, struct bt_smp, work);
BT_ERR("SMP Timeout");
/*
* If SMP timeout occurred during key distribution we should assume
* pairing failed and don't store any keys from this pairing.
*/
if (atomic_test_bit(smp->flags, SMP_FLAG_KEYS_DISTR) &&
smp->chan.chan.conn->keys) {
bt_keys_clear(smp->chan.chan.conn->keys, BT_KEYS_ALL);
}
smp_reset(smp);
atomic_set_bit(smp->flags, SMP_FLAG_TIMEOUT);
}
static struct net_buf *smp_create_pdu(struct bt_conn *conn, uint8_t op,
size_t len)
{
struct bt_smp_hdr *hdr;
struct net_buf *buf;
buf = bt_l2cap_create_pdu(&smp_buf);
if (!buf) {
return NULL;
}
hdr = net_buf_add(buf, sizeof(*hdr));
hdr->code = op;
return buf;
}
static void smp_send(struct bt_smp *smp, struct net_buf *buf)
{
bt_l2cap_send(smp->chan.chan.conn, BT_L2CAP_CID_SMP, buf);
nano_delayed_work_submit(&smp->work, SMP_TIMEOUT);
}
static int smp_error(struct bt_smp *smp, uint8_t reason)
{
struct bt_smp_pairing_fail *rsp;
struct net_buf *buf;
/* reset context */
smp_reset(smp);
buf = smp_create_pdu(smp->chan.chan.conn, BT_SMP_CMD_PAIRING_FAIL,
sizeof(*rsp));
if (!buf) {
return -ENOBUFS;
}
rsp = net_buf_add(buf, sizeof(*rsp));
rsp->reason = reason;
/* SMP timer is not restarted for PairingFailed so don't use smp_send */
bt_l2cap_send(smp->chan.chan.conn, BT_L2CAP_CID_SMP, buf);
return 0;
}
static uint8_t smp_send_pairing_random(struct bt_smp *smp)
{
struct bt_conn *conn = smp->chan.chan.conn;
struct bt_smp_pairing_random *req;
struct net_buf *rsp_buf;
rsp_buf = smp_create_pdu(conn, BT_SMP_CMD_PAIRING_RANDOM, sizeof(*req));
if (!rsp_buf) {
return BT_SMP_ERR_UNSPECIFIED;
}
req = net_buf_add(rsp_buf, sizeof(*req));
memcpy(req->val, smp->prnd, sizeof(req->val));
smp_send(smp, rsp_buf);
return 0;
}
static uint8_t get_encryption_key_size(struct bt_smp *smp)
{
struct bt_smp_pairing *req, *rsp;
req = (struct bt_smp_pairing *)&smp->preq[1];
rsp = (struct bt_smp_pairing *)&smp->prsp[1];
/*
* The smaller value of the initiating and responding devices maximum
* encryption key length parameters shall be used as the encryption key
* size.
*/
return min(req->max_key_size, rsp->max_key_size);
}
#if !defined(CONFIG_BLUETOOTH_SMP_SC_ONLY)
static void xor_128(const uint8_t p[16], const uint8_t q[16], uint8_t r[16])
{
size_t len = 16;
while (len--) {
*r++ = *p++ ^ *q++;
}
}
static int smp_c1(const uint8_t k[16], const uint8_t r[16],
const uint8_t preq[7], const uint8_t pres[7],
const bt_addr_le_t *ia, const bt_addr_le_t *ra,
uint8_t enc_data[16])
{
uint8_t p1[16], p2[16];
int err;
BT_DBG("k %s r %s", bt_hex(k, 16), bt_hex(r, 16));
BT_DBG("ia %s ra %s", bt_addr_le_str(ia), bt_addr_le_str(ra));
BT_DBG("preq %s pres %s", bt_hex(preq, 7), bt_hex(pres, 7));
/* pres, preq, rat and iat are concatenated to generate p1 */
p1[0] = ia->type;
p1[1] = ra->type;
memcpy(p1 + 2, preq, 7);
memcpy(p1 + 9, pres, 7);
BT_DBG("p1 %s", bt_hex(p1, 16));
/* c1 = e(k, e(k, r XOR p1) XOR p2) */
/* Using enc_data as temporary output buffer */
xor_128(r, p1, enc_data);
err = le_encrypt(k, enc_data, enc_data);
if (err) {
return err;
}
/* ra is concatenated with ia and padding to generate p2 */
memcpy(p2, ra->a.val, 6);
memcpy(p2 + 6, ia->a.val, 6);
memset(p2 + 12, 0, 4);
BT_DBG("p2 %s", bt_hex(p2, 16));
xor_128(enc_data, p2, enc_data);
return le_encrypt(k, enc_data, enc_data);
}
#endif /* !CONFIG_BLUETOOTH_SMP_SC_ONLY */
static uint8_t smp_send_pairing_confirm(struct bt_smp *smp)
{
struct bt_conn *conn = smp->chan.chan.conn;
struct bt_smp_pairing_confirm *req;
struct net_buf *buf;
uint8_t r;
switch (smp->method) {
case PASSKEY_CONFIRM:
case JUST_WORKS:
r = 0;
break;
case PASSKEY_DISPLAY:
case PASSKEY_INPUT:
/*
* In the Passkey Entry protocol, the most significant
* bit of Z is set equal to one and the least
* significant bit is made up from one bit of the
* passkey e.g. if the passkey bit is 1, then Z = 0x81
* and if the passkey bit is 0, then Z = 0x80.
*/
r = (smp->passkey >> smp->passkey_round) & 0x01;
r |= 0x80;
break;
default:
return BT_SMP_ERR_UNSPECIFIED;
}
buf = smp_create_pdu(conn, BT_SMP_CMD_PAIRING_CONFIRM, sizeof(*req));
if (!buf) {
return BT_SMP_ERR_UNSPECIFIED;
}
req = net_buf_add(buf, sizeof(*req));
if (smp_f4(sc_public_key, smp->pkey, smp->prnd, r, req->val)) {
net_buf_unref(buf);
return BT_SMP_ERR_UNSPECIFIED;
}
smp_send(smp, buf);
atomic_clear_bit(smp->flags, SMP_FLAG_CFM_DELAYED);
return 0;
}
#if !defined(CONFIG_BLUETOOTH_SMP_SC_ONLY)
static void legacy_distribute_keys(struct bt_smp *smp)
{
struct bt_conn *conn = smp->chan.chan.conn;
struct bt_keys *keys = conn->keys;
if (smp->local_dist & BT_SMP_DIST_ENC_KEY) {
struct bt_smp_encrypt_info *info;
struct bt_smp_master_ident *ident;
struct net_buf *buf;
uint8_t key[16];
uint64_t rand;
uint16_t ediv;
smp->local_dist &= ~BT_SMP_DIST_ENC_KEY;
bt_rand(key, sizeof(key));
bt_rand(&rand, sizeof(rand));
bt_rand(&ediv, sizeof(ediv));
buf = smp_create_pdu(conn, BT_SMP_CMD_ENCRYPT_INFO,
sizeof(*info));
if (!buf) {
BT_ERR("Unable to allocate Encrypt Info buffer");
return;
}
info = net_buf_add(buf, sizeof(*info));
/* distributed only enc_size bytes of key */
memcpy(info->ltk, key, keys->enc_size);
if (keys->enc_size < sizeof(info->ltk)) {
memset(info->ltk + keys->enc_size, 0,
sizeof(info->ltk) - keys->enc_size);
}
smp_send(smp, buf);
buf = smp_create_pdu(conn, BT_SMP_CMD_MASTER_IDENT,
sizeof(*ident));
if (!buf) {
BT_ERR("Unable to allocate Master Ident buffer");
return;
}
ident = net_buf_add(buf, sizeof(*ident));
ident->rand = rand;
ident->ediv = ediv;
smp_send(smp, buf);
if (atomic_test_bit(smp->flags, SMP_FLAG_BOND)) {
bt_keys_add_type(keys, BT_KEYS_SLAVE_LTK);
memcpy(keys->slave_ltk.val, key,
sizeof(keys->slave_ltk.val));
keys->slave_ltk.rand = rand;
keys->slave_ltk.ediv = ediv;
}
}
}
#endif /* !CONFIG_BLUETOOTH_SMP_SC_ONLY */
static void bt_smp_distribute_keys(struct bt_smp *smp)
{
struct bt_conn *conn = smp->chan.chan.conn;
struct bt_keys *keys = conn->keys;
if (!keys) {
BT_ERR("No keys space for %s", bt_addr_le_str(&conn->le.dst));
return;
}
#if !defined(CONFIG_BLUETOOTH_SMP_SC_ONLY)
/* Distribute legacy pairing specific keys */
if (!atomic_test_bit(smp->flags, SMP_FLAG_SC)) {
legacy_distribute_keys(smp);
}
#endif /* !CONFIG_BLUETOOTH_SMP_SC_ONLY */
#if defined(CONFIG_BLUETOOTH_PRIVACY)
if (smp->local_dist & BT_SMP_DIST_ID_KEY) {
struct bt_smp_ident_info *id_info;
struct bt_smp_ident_addr_info *id_addr_info;
struct net_buf *buf;
smp->local_dist &= ~BT_SMP_DIST_ID_KEY;
buf = smp_create_pdu(conn, BT_SMP_CMD_IDENT_INFO,
sizeof(*id_info));
if (!buf) {
BT_ERR("Unable to allocate Ident Info buffer");
return;
}
id_info = net_buf_add(buf, sizeof(*id_info));
memcpy(id_info->irk, bt_dev.irk, 16);
smp_send(smp, buf);
buf = smp_create_pdu(conn, BT_SMP_CMD_IDENT_ADDR_INFO,
sizeof(*id_addr_info));
if (!buf) {
BT_ERR("Unable to allocate Ident Addr Info buffer");
return;
}
id_addr_info = net_buf_add(buf, sizeof(*id_addr_info));
bt_addr_le_copy(&id_addr_info->addr, &bt_dev.id_addr);
smp_send(smp, buf);
}
#endif /* CONFIG_BLUETOOTH_PRIVACY */
#if defined(CONFIG_BLUETOOTH_SIGNING)
if (smp->local_dist & BT_SMP_DIST_SIGN) {
struct bt_smp_signing_info *info;
struct net_buf *buf;
smp->local_dist &= ~BT_SMP_DIST_SIGN;
buf = smp_create_pdu(conn, BT_SMP_CMD_SIGNING_INFO,
sizeof(*info));
if (!buf) {
BT_ERR("Unable to allocate Signing Info buffer");
return;
}
info = net_buf_add(buf, sizeof(*info));
bt_rand(info->csrk, sizeof(info->csrk));
if (atomic_test_bit(smp->flags, SMP_FLAG_BOND)) {
bt_keys_add_type(keys, BT_KEYS_LOCAL_CSRK);
memcpy(keys->local_csrk.val, info->csrk, 16);
keys->local_csrk.cnt = 0;
}
smp_send(smp, buf);
}
#endif /* CONFIG_BLUETOOTH_SIGNING */
}
#if defined(CONFIG_BLUETOOTH_PERIPHERAL)
static uint8_t send_pairing_rsp(struct bt_smp *smp)
{
struct bt_conn *conn = smp->chan.chan.conn;
struct bt_smp_pairing *rsp;
struct net_buf *rsp_buf;
rsp_buf = smp_create_pdu(conn, BT_SMP_CMD_PAIRING_RSP, sizeof(*rsp));
if (!rsp_buf) {
return BT_SMP_ERR_UNSPECIFIED;
}
rsp = net_buf_add(rsp_buf, sizeof(*rsp));
memcpy(rsp, smp->prsp + 1, sizeof(*rsp));
smp_send(smp, rsp_buf);
return 0;
}
#endif /* CONFIG_BLUETOOTH_PERIPHERAL */
#if !defined(CONFIG_BLUETOOTH_SMP_SC_ONLY)
static int smp_s1(const uint8_t k[16], const uint8_t r1[16],
const uint8_t r2[16], uint8_t out[16])
{
/* The most significant 64-bits of r1 are discarded to generate
* r1' and the most significant 64-bits of r2 are discarded to
* generate r2'.
* r1' is concatenated with r2' to generate r' which is used as
* the 128-bit input parameter plaintextData to security function e:
*
* r' = r1' || r2'
*/
memcpy(out, r2, 8);
memcpy(out + 8, r1, 8);
/* s1(k, r1 , r2) = e(k, r') */
return le_encrypt(k, out, out);
}
static uint8_t legacy_get_pair_method(struct bt_smp *smp, uint8_t remote_io)
{
struct bt_smp_pairing *req, *rsp;
uint8_t method;
if (remote_io > BT_SMP_IO_KEYBOARD_DISPLAY)
return JUST_WORKS;
req = (struct bt_smp_pairing *)&smp->preq[1];
rsp = (struct bt_smp_pairing *)&smp->prsp[1];
/* if none side requires MITM use JustWorks */
if (!((req->auth_req | rsp->auth_req) & BT_SMP_AUTH_MITM)) {
return JUST_WORKS;
}
method = gen_method_legacy[remote_io][get_io_capa()];
/* if both sides have KeyboardDisplay capabilities, initiator displays
* and responder inputs
*/
if (method == PASSKEY_ROLE) {
if (smp->chan.chan.conn->role == BT_HCI_ROLE_MASTER) {
method = PASSKEY_DISPLAY;
} else {
method = PASSKEY_INPUT;
}
}
return method;
}
static uint8_t legacy_request_tk(struct bt_smp *smp)
{
struct bt_conn *conn = smp->chan.chan.conn;
struct bt_keys *keys;
uint32_t passkey;
/*
* Fail if we have keys that are stronger than keys that will be
* distributed in new pairing. This is to avoid replacing authenticated
* keys with unauthenticated ones.
*/
keys = bt_keys_find_addr(&conn->le.dst);
if (keys && atomic_test_bit(keys->flags, BT_KEYS_AUTHENTICATED) &&
smp->method == JUST_WORKS) {
BT_ERR("JustWorks failed, authenticated keys present");
return BT_SMP_ERR_UNSPECIFIED;
}
switch (smp->method) {
case PASSKEY_DISPLAY:
if (bt_rand(&passkey, sizeof(passkey))) {
return BT_SMP_ERR_UNSPECIFIED;
}
passkey %= 1000000;
bt_auth->passkey_display(conn, passkey);
passkey = sys_cpu_to_le32(passkey);
memcpy(smp->tk, &passkey, sizeof(passkey));
break;
case PASSKEY_INPUT:
atomic_set_bit(smp->flags, SMP_FLAG_USER);
bt_auth->passkey_entry(conn);
break;
case JUST_WORKS:
break;
default:
BT_ERR("Unknown pairing method (%u)", smp->method);
return BT_SMP_ERR_UNSPECIFIED;
}
return 0;
}
static uint8_t legacy_send_pairing_confirm(struct bt_smp *smp)
{
struct bt_conn *conn = smp->chan.chan.conn;
struct bt_smp_pairing_confirm *req;
struct net_buf *buf;
buf = smp_create_pdu(conn, BT_SMP_CMD_PAIRING_CONFIRM, sizeof(*req));
if (!buf) {
return BT_SMP_ERR_UNSPECIFIED;
}
req = net_buf_add(buf, sizeof(*req));
if (smp_c1(smp->tk, smp->prnd, smp->preq, smp->prsp,
&conn->le.init_addr, &conn->le.resp_addr, req->val)) {
net_buf_unref(buf);
return BT_SMP_ERR_UNSPECIFIED;
}
smp_send(smp, buf);
atomic_clear_bit(smp->flags, SMP_FLAG_CFM_DELAYED);
return 0;
}
#if defined(CONFIG_BLUETOOTH_PERIPHERAL)
static uint8_t legacy_pairing_req(struct bt_smp *smp, uint8_t remote_io)
{
uint8_t ret;
BT_DBG("");
smp->method = legacy_get_pair_method(smp, remote_io);
/* ask for consent if pairing is not due to sending SecReq*/
if (smp->method == JUST_WORKS &&
!atomic_test_bit(smp->flags, SMP_FLAG_SEC_REQ) &&
bt_auth && bt_auth->pairing_confirm) {
atomic_set_bit(smp->flags, SMP_FLAG_USER);
bt_auth->pairing_confirm(smp->chan.chan.conn);
return 0;
}
ret = send_pairing_rsp(smp);
if (ret) {
return ret;
}
atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_PAIRING_CONFIRM);
return legacy_request_tk(smp);
}
#endif /* CONFIG_BLUETOOTH_PERIPHERAL */
static uint8_t legacy_pairing_random(struct bt_smp *smp)
{
struct bt_conn *conn = smp->chan.chan.conn;
uint8_t tmp[16];
int err;
BT_DBG("");
/* calculate confirmation */
err = smp_c1(smp->tk, smp->rrnd, smp->preq, smp->prsp,
&conn->le.init_addr, &conn->le.resp_addr, tmp);
if (err) {
return BT_SMP_ERR_UNSPECIFIED;
}
BT_DBG("pcnf %s cfm %s", bt_hex(smp->pcnf, 16), bt_hex(tmp, 16));
if (memcmp(smp->pcnf, tmp, sizeof(smp->pcnf))) {
return BT_SMP_ERR_CONFIRM_FAILED;
}
#if defined(CONFIG_BLUETOOTH_CENTRAL)
if (conn->role == BT_HCI_ROLE_MASTER) {
/* No need to store master STK */
err = smp_s1(smp->tk, smp->rrnd, smp->prnd, tmp);
if (err) {
return BT_SMP_ERR_UNSPECIFIED;
}
/* Rand and EDiv are 0 for the STK */
if (bt_conn_le_start_encryption(conn, 0, 0, tmp,
get_encryption_key_size(smp))) {
BT_ERR("Failed to start encryption");
return BT_SMP_ERR_UNSPECIFIED;
}
atomic_set_bit(smp->flags, SMP_FLAG_ENC_PENDING);
return 0;
}
#endif /* CONFIG_BLUETOOTH_CENTRAL */
#if defined(CONFIG_BLUETOOTH_PERIPHERAL)
err = smp_s1(smp->tk, smp->prnd, smp->rrnd, tmp);
if (err) {
return BT_SMP_ERR_UNSPECIFIED;
}
memcpy(smp->tk, tmp, sizeof(smp->tk));
BT_DBG("generated STK %s", bt_hex(smp->tk, 16));
atomic_set_bit(smp->flags, SMP_FLAG_ENC_PENDING);
smp_send_pairing_random(smp);
#endif /* CONFIG_BLUETOOTH_PERIPHERAL */
return 0;
}
static uint8_t legacy_pairing_confirm(struct bt_smp *smp)
{
BT_DBG("");
#if defined(CONFIG_BLUETOOTH_CENTRAL)
if (smp->chan.chan.conn->role == BT_HCI_ROLE_MASTER) {
atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_PAIRING_CONFIRM);
return legacy_send_pairing_confirm(smp);
}
#endif /* CONFIG_BLUETOOTH_CENTRAL */
#if defined(CONFIG_BLUETOOTH_PERIPHERAL)
if (!atomic_test_bit(smp->flags, SMP_FLAG_USER)) {
atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_PAIRING_RANDOM);
return legacy_send_pairing_confirm(smp);
}
atomic_set_bit(smp->flags, SMP_FLAG_CFM_DELAYED);
#endif /* CONFIG_BLUETOOTH_PERIPHERAL */
return 0;
}
static void legacy_passkey_entry(struct bt_smp *smp, unsigned int passkey)
{
passkey = sys_cpu_to_le32(passkey);
memcpy(smp->tk, &passkey, sizeof(passkey));
if (!atomic_test_and_clear_bit(smp->flags, SMP_FLAG_CFM_DELAYED)) {
smp_error(smp, BT_SMP_ERR_PASSKEY_ENTRY_FAILED);
return;
}
/* if confirm failed ie. due to invalid passkey, cancel pairing */
if (legacy_pairing_confirm(smp)) {
smp_error(smp, BT_SMP_ERR_PASSKEY_ENTRY_FAILED);
return;
}
#if defined(CONFIG_BLUETOOTH_CENTRAL)
if (smp->chan.chan.conn->role == BT_HCI_ROLE_MASTER) {
atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_PAIRING_CONFIRM);
return;
}
#endif /* CONFIG_BLUETOOTH_CENTRAL */
#if defined(CONFIG_BLUETOOTH_PERIPHERAL)
atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_PAIRING_RANDOM);
#endif /* CONFIG_BLUETOOTH_PERIPHERAL */
}
static uint8_t smp_encrypt_info(struct bt_smp *smp, struct net_buf *buf)
{
BT_DBG("");
if (atomic_test_bit(smp->flags, SMP_FLAG_BOND)) {
struct bt_smp_encrypt_info *req = (void *)buf->data;
struct bt_conn *conn = smp->chan.chan.conn;
struct bt_keys *keys;
keys = bt_keys_get_type(BT_KEYS_LTK, &conn->le.dst);
if (!keys) {
BT_ERR("Unable to get keys for %s",
bt_addr_le_str(&conn->le.dst));
return BT_SMP_ERR_UNSPECIFIED;
}
memcpy(keys->ltk.val, req->ltk, 16);
}
atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_MASTER_IDENT);
return 0;
}
static uint8_t smp_master_ident(struct bt_smp *smp, struct net_buf *buf)
{
struct bt_conn *conn = smp->chan.chan.conn;
BT_DBG("");
if (atomic_test_bit(smp->flags, SMP_FLAG_BOND)) {
struct bt_smp_master_ident *req = (void *)buf->data;
struct bt_keys *keys;
keys = bt_keys_get_type(BT_KEYS_LTK, &conn->le.dst);
if (!keys) {
BT_ERR("Unable to get keys for %s",
bt_addr_le_str(&conn->le.dst));
return BT_SMP_ERR_UNSPECIFIED;
}
keys->ltk.ediv = req->ediv;
keys->ltk.rand = req->rand;
smp->remote_dist &= ~BT_SMP_DIST_ENC_KEY;
}
if (smp->remote_dist & BT_SMP_DIST_ID_KEY) {
atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_IDENT_INFO);
} else if (smp->remote_dist & BT_SMP_DIST_SIGN) {
atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_SIGNING_INFO);
}
#if defined(CONFIG_BLUETOOTH_CENTRAL)
if (conn->role == BT_HCI_ROLE_MASTER && !smp->remote_dist) {
bt_smp_distribute_keys(smp);
}
#endif /* CONFIG_BLUETOOTH_CENTRAL */
/* if all keys were distributed, pairing is done */
if (!smp->local_dist && !smp->remote_dist) {
smp_reset(smp);
}
return 0;
}
#if defined(CONFIG_BLUETOOTH_CENTRAL)
static uint8_t legacy_pairing_rsp(struct bt_smp *smp, uint8_t remote_io)
{
uint8_t ret;
BT_DBG("");
smp->method = legacy_get_pair_method(smp, remote_io);
/* ask for consent if this is due to received SecReq */
if (smp->method == JUST_WORKS &&
atomic_test_bit(smp->flags, SMP_FLAG_SEC_REQ) &&
bt_auth && bt_auth->pairing_confirm) {
atomic_set_bit(smp->flags, SMP_FLAG_USER);
bt_auth->pairing_confirm(smp->chan.chan.conn);
return 0;
}
ret = legacy_request_tk(smp);
if (ret) {
return ret;
}
if (!atomic_test_bit(smp->flags, SMP_FLAG_USER)) {
atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_PAIRING_CONFIRM);
return legacy_send_pairing_confirm(smp);
}
atomic_set_bit(smp->flags, SMP_FLAG_CFM_DELAYED);
return 0;
}
#endif /* CONFIG_BLUETOOTH_CENTRAL */
#else
static uint8_t smp_encrypt_info(struct bt_smp *smp, struct net_buf *buf)
{
return BT_SMP_ERR_CMD_NOTSUPP;
}
static uint8_t smp_master_ident(struct bt_smp *smp, struct net_buf *buf)
{
return BT_SMP_ERR_CMD_NOTSUPP;
}
#endif /* !CONFIG_BLUETOOTH_SMP_SC_ONLY */
static int smp_init(struct bt_smp *smp)
{
/* Initialize SMP context without clearing L2CAP channel context */
memset((uint8_t *)smp + sizeof(smp->chan), 0,
sizeof(*smp) - (sizeof(smp->chan) + sizeof(smp->work)));
/* Generate local random number */
if (bt_rand(smp->prnd, 16)) {
return BT_SMP_ERR_UNSPECIFIED;
}
BT_DBG("prnd %s", bt_hex(smp->prnd, 16));
atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_PAIRING_FAIL);
return 0;
}
static uint8_t get_auth(uint8_t auth)
{
if (sc_supported) {
auth &= BT_SMP_AUTH_MASK_SC;
} else {
auth &= BT_SMP_AUTH_MASK;
}
if (get_io_capa() == BT_SMP_IO_NO_INPUT_OUTPUT) {
auth &= ~(BT_SMP_AUTH_MITM);
} else {
auth |= BT_SMP_AUTH_MITM;
}
return auth;
}
static bool sec_level_reachable(struct bt_conn *conn)
{
switch (conn->required_sec_level) {
case BT_SECURITY_LOW:
case BT_SECURITY_MEDIUM:
return true;
case BT_SECURITY_HIGH:
return get_io_capa() != BT_SMP_IO_NO_INPUT_OUTPUT;
case BT_SECURITY_FIPS:
return get_io_capa() != BT_SMP_IO_NO_INPUT_OUTPUT &&
sc_supported;
default:
return false;
}
}
static struct bt_smp *smp_chan_get(struct bt_conn *conn)
{
struct bt_l2cap_chan *chan;
chan = bt_l2cap_le_lookup_rx_cid(conn, BT_L2CAP_CID_SMP);
if (!chan) {
BT_ERR("Unable to find SMP channel");
return NULL;
}
return CONTAINER_OF(chan, struct bt_smp, chan);
}
#if defined(CONFIG_BLUETOOTH_PERIPHERAL)
int bt_smp_send_security_req(struct bt_conn *conn)
{
struct bt_smp *smp;
struct bt_smp_security_request *req;
struct net_buf *req_buf;
BT_DBG("");
smp = smp_chan_get(conn);
if (!smp) {
return -ENOTCONN;
}
/* SMP Timeout */
if (atomic_test_bit(smp->flags, SMP_FLAG_TIMEOUT)) {
return -EIO;
}
/* pairing is in progress */
if (atomic_test_bit(smp->flags, SMP_FLAG_PAIRING)) {
return -EBUSY;
}
/* early verify if required sec level if reachable */
if (!sec_level_reachable(conn)) {
return -EINVAL;
}
req_buf = smp_create_pdu(conn, BT_SMP_CMD_SECURITY_REQUEST,
sizeof(*req));
if (!req_buf) {
return -ENOBUFS;
}
req = net_buf_add(req_buf, sizeof(*req));
req->auth_req = get_auth(BT_SMP_AUTH_BONDING | BT_SMP_AUTH_SC);
/* SMP timer is not restarted for SecRequest so don't use smp_send */
bt_l2cap_send(conn, BT_L2CAP_CID_SMP, req_buf);
atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_PAIRING_FAIL);
return 0;
}
static uint8_t smp_pairing_req(struct bt_smp *smp, struct net_buf *buf)
{
struct bt_smp_pairing *req = (void *)buf->data;
struct bt_smp_pairing *rsp;
int ret;
BT_DBG("");
if ((req->max_key_size > BT_SMP_MAX_ENC_KEY_SIZE) ||
(req->max_key_size < BT_SMP_MIN_ENC_KEY_SIZE)) {
return BT_SMP_ERR_ENC_KEY_SIZE;
}
ret = smp_init(smp);
if (ret) {
return ret;
}
/* Store req for later use */
smp->preq[0] = BT_SMP_CMD_PAIRING_REQ;
memcpy(smp->preq + 1, req, sizeof(*req));
/* create rsp, it will be used later on */
smp->prsp[0] = BT_SMP_CMD_PAIRING_RSP;
rsp = (struct bt_smp_pairing *)&smp->prsp[1];
rsp->auth_req = get_auth(req->auth_req);
rsp->io_capability = get_io_capa();
rsp->oob_flag = BT_SMP_OOB_NOT_PRESENT;
rsp->max_key_size = BT_SMP_MAX_ENC_KEY_SIZE;
rsp->init_key_dist = (req->init_key_dist & RECV_KEYS);
rsp->resp_key_dist = (req->resp_key_dist & SEND_KEYS);
if ((rsp->auth_req & BT_SMP_AUTH_SC) &&
(req->auth_req & BT_SMP_AUTH_SC)) {
atomic_set_bit(smp->flags, SMP_FLAG_SC);
rsp->init_key_dist &= RECV_KEYS_SC;
rsp->resp_key_dist &= SEND_KEYS_SC;
}
smp->local_dist = rsp->resp_key_dist;
smp->remote_dist = rsp->init_key_dist;
if ((rsp->auth_req & BT_SMP_AUTH_BONDING) &&
(req->auth_req & BT_SMP_AUTH_BONDING)) {
atomic_set_bit(smp->flags, SMP_FLAG_BOND);
}
atomic_set_bit(smp->flags, SMP_FLAG_PAIRING);
if (!atomic_test_bit(smp->flags, SMP_FLAG_SC)) {
#if defined(CONFIG_BLUETOOTH_SMP_SC_ONLY)
return BT_SMP_ERR_AUTH_REQUIREMENTS;
#else
return legacy_pairing_req(smp, req->io_capability);
#endif/* CONFIG_BLUETOOTH_SMP_SC_ONLY */
}
smp->method = get_pair_method(smp, req->io_capability);
#if defined(CONFIG_BLUETOOTH_SMP_SC_ONLY)
if (smp->method == JUST_WORKS) {
return BT_SMP_ERR_AUTH_REQUIREMENTS;
}
#endif/* CONFIG_BLUETOOTH_SMP_SC_ONLY */
if (smp->method == JUST_WORKS) {
#if defined(CONFIG_BLUETOOTH_SMP_SC_ONLY)
return BT_SMP_ERR_AUTH_REQUIREMENTS;
#else
/* ask for consent if pairing is not due to sending SecReq*/
if (!atomic_test_bit(smp->flags, SMP_FLAG_SEC_REQ) &&
bt_auth && bt_auth->pairing_confirm) {
atomic_set_bit(smp->flags, SMP_FLAG_USER);
bt_auth->pairing_confirm(smp->chan.chan.conn);
return 0;
}
#endif/* CONFIG_BLUETOOTH_SMP_SC_ONLY */
}
atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_PUBLIC_KEY);
return send_pairing_rsp(smp);
}
#else
static uint8_t smp_pairing_req(struct bt_smp *smp, struct net_buf *buf)
{
return BT_SMP_ERR_CMD_NOTSUPP;
}
#endif /* CONFIG_BLUETOOTH_PERIPHERAL */
static uint8_t sc_send_public_key(struct bt_smp *smp)
{
struct bt_smp_public_key *req;
struct net_buf *req_buf;
req_buf = smp_create_pdu(smp->chan.chan.conn, BT_SMP_CMD_PUBLIC_KEY,
sizeof(*req));
if (!req_buf) {
return BT_SMP_ERR_UNSPECIFIED;
}
req = net_buf_add(req_buf, sizeof(*req));
memcpy(req->x, sc_public_key, sizeof(req->x));
memcpy(req->y, &sc_public_key[32], sizeof(req->y));
smp_send(smp, req_buf);
#if defined(CONFIG_BLUETOOTH_USE_DEBUG_KEYS)
atomic_set_bit(smp->flags, SMP_FLAG_SC_DEBUG_KEY);
#endif /* CONFIG_BLUETOOTH_USE_DEBUG_KEYS */
return 0;
}
#if defined(CONFIG_BLUETOOTH_CENTRAL)
int bt_smp_send_pairing_req(struct bt_conn *conn)
{
struct bt_smp *smp;
struct bt_smp_pairing *req;
struct net_buf *req_buf;
BT_DBG("");
smp = smp_chan_get(conn);
if (!smp) {
return -ENOTCONN;
}
/* SMP Timeout */
if (atomic_test_bit(smp->flags, SMP_FLAG_TIMEOUT)) {
return -EIO;
}
/* pairing is in progress */
if (atomic_test_bit(smp->flags, SMP_FLAG_PAIRING)) {
return -EBUSY;
}
/* early verify if required sec level if reachable */
if (!sec_level_reachable(conn)) {
return -EINVAL;
}
if (smp_init(smp)) {
return -ENOBUFS;
}
req_buf = smp_create_pdu(conn, BT_SMP_CMD_PAIRING_REQ, sizeof(*req));
if (!req_buf) {
return -ENOBUFS;
}
req = net_buf_add(req_buf, sizeof(*req));
req->auth_req = get_auth(BT_SMP_AUTH_BONDING | BT_SMP_AUTH_SC);
req->io_capability = get_io_capa();
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;
/* Store req for later use */
smp->preq[0] = BT_SMP_CMD_PAIRING_REQ;
memcpy(smp->preq + 1, req, sizeof(*req));
smp_send(smp, req_buf);
atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_PAIRING_RSP);
atomic_set_bit(smp->flags, SMP_FLAG_PAIRING);
return 0;
}
static uint8_t smp_pairing_rsp(struct bt_smp *smp, struct net_buf *buf)
{
struct bt_smp_pairing *rsp = (void *)buf->data;
struct bt_smp_pairing *req = (struct bt_smp_pairing *)&smp->preq[1];
BT_DBG("");
if ((rsp->max_key_size > BT_SMP_MAX_ENC_KEY_SIZE) ||
(rsp->max_key_size < BT_SMP_MIN_ENC_KEY_SIZE)) {
return BT_SMP_ERR_ENC_KEY_SIZE;
}
smp->local_dist &= rsp->init_key_dist;
smp->remote_dist &= rsp->resp_key_dist;
/* Store rsp for later use */
smp->prsp[0] = BT_SMP_CMD_PAIRING_RSP;
memcpy(smp->prsp + 1, rsp, sizeof(*rsp));
if ((rsp->auth_req & BT_SMP_AUTH_SC) &&
(req->auth_req & BT_SMP_AUTH_SC)) {
atomic_set_bit(smp->flags, SMP_FLAG_SC);
}
if ((rsp->auth_req & BT_SMP_AUTH_BONDING) &&
(req->auth_req & BT_SMP_AUTH_BONDING)) {
atomic_set_bit(smp->flags, SMP_FLAG_BOND);
}
if (!atomic_test_bit(smp->flags, SMP_FLAG_SC)) {
#if defined(CONFIG_BLUETOOTH_SMP_SC_ONLY)
return BT_SMP_ERR_AUTH_REQUIREMENTS;
#else
return legacy_pairing_rsp(smp, rsp->io_capability);
#endif /* CONFIG_BLUETOOTH_SMP_SC_ONLY */
}
smp->method = get_pair_method(smp, rsp->io_capability);
smp->local_dist &= SEND_KEYS_SC;
smp->remote_dist &= RECV_KEYS_SC;
if (smp->method == JUST_WORKS) {
#if defined(CONFIG_BLUETOOTH_SMP_SC_ONLY)
return BT_SMP_ERR_AUTH_REQUIREMENTS;
#else
/* ask for consent if this is due to received SecReq */
if (atomic_test_bit(smp->flags, SMP_FLAG_SEC_REQ) &&
bt_auth && bt_auth->pairing_confirm) {
atomic_set_bit(smp->flags, SMP_FLAG_USER);
bt_auth->pairing_confirm(smp->chan.chan.conn);
return 0;
}
#endif/* CONFIG_BLUETOOTH_SMP_SC_ONLY */
}
if (!sc_local_pkey_valid) {
atomic_set_bit(smp->flags, SMP_FLAG_PKEY_SEND);
return 0;
}
atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_PUBLIC_KEY);
return sc_send_public_key(smp);
}
#else
static uint8_t smp_pairing_rsp(struct bt_smp *smp, struct net_buf *buf)
{
return BT_SMP_ERR_CMD_NOTSUPP;
}
#endif /* CONFIG_BLUETOOTH_CENTRAL */
static uint8_t smp_pairing_confirm(struct bt_smp *smp, struct net_buf *buf)
{
struct bt_smp_pairing_confirm *req = (void *)buf->data;
BT_DBG("");
memcpy(smp->pcnf, req->val, sizeof(smp->pcnf));
#if defined(CONFIG_BLUETOOTH_CENTRAL)
if (smp->chan.chan.conn->role == BT_HCI_ROLE_MASTER) {
atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_PAIRING_RANDOM);
return smp_send_pairing_random(smp);
}
#endif /* CONFIG_BLUETOOTH_CENTRAL */
#if defined(CONFIG_BLUETOOTH_PERIPHERAL)
#if !defined(CONFIG_BLUETOOTH_SMP_SC_ONLY)
if (!atomic_test_bit(smp->flags, SMP_FLAG_SC)) {
return legacy_pairing_confirm(smp);
}
#endif /* !CONFIG_BLUETOOTH_SMP_SC_ONLY */
switch (smp->method) {
case PASSKEY_DISPLAY:
atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_PAIRING_RANDOM);
return smp_send_pairing_confirm(smp);
case PASSKEY_INPUT:
if (atomic_test_bit(smp->flags, SMP_FLAG_USER)) {
atomic_set_bit(smp->flags, SMP_FLAG_CFM_DELAYED);
return 0;
}
atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_PAIRING_RANDOM);
return smp_send_pairing_confirm(smp);
case JUST_WORKS:
case PASSKEY_CONFIRM:
default:
return BT_SMP_ERR_UNSPECIFIED;
}
#endif /* CONFIG_BLUETOOTH_PERIPHERAL */
return 0;
}
static uint8_t sc_smp_send_dhkey_check(struct bt_smp *smp, const uint8_t *e)
{
struct bt_smp_dhkey_check *req;
struct net_buf *buf;
BT_DBG("");
buf = smp_create_pdu(smp->chan.chan.conn, BT_SMP_DHKEY_CHECK,
sizeof(*req));
if (!buf) {
return BT_SMP_ERR_UNSPECIFIED;
}
req = net_buf_add(buf, sizeof(*req));
memcpy(req->e, e, sizeof(req->e));
smp_send(smp, buf);
return 0;
}
#if defined(CONFIG_BLUETOOTH_CENTRAL)
static uint8_t compute_and_send_master_dhcheck(struct bt_smp *smp)
{
uint8_t e[16], r[16];
memset(r, 0, sizeof(r));
switch (smp->method) {
case JUST_WORKS:
case PASSKEY_CONFIRM:
break;
case PASSKEY_DISPLAY:
case PASSKEY_INPUT:
memcpy(r, &smp->passkey, sizeof(smp->passkey));
break;
default:
return BT_SMP_ERR_UNSPECIFIED;
}
/* calculate LTK and mackey */
if (smp_f5(smp->dhkey, smp->prnd, smp->rrnd,
&smp->chan.chan.conn->le.init_addr,
&smp->chan.chan.conn->le.resp_addr, smp->mackey,
smp->tk)) {
return BT_SMP_ERR_UNSPECIFIED;
}
/* calculate local DHKey check */
if (smp_f6(smp->mackey, smp->prnd, smp->rrnd, r, &smp->preq[1],
&smp->chan.chan.conn->le.init_addr,
&smp->chan.chan.conn->le.resp_addr, e)) {
return BT_SMP_ERR_UNSPECIFIED;
}
atomic_set_bit(&smp->allowed_cmds, BT_SMP_DHKEY_CHECK);
sc_smp_send_dhkey_check(smp, e);
return 0;
}
#endif /* CONFIG_BLUETOOTH_CENTRAL */
#if defined(CONFIG_BLUETOOTH_PERIPHERAL)
static uint8_t compute_and_check_and_send_slave_dhcheck(struct bt_smp *smp)
{
uint8_t re[16], e[16], r[16];
memset(r, 0, sizeof(r));
switch (smp->method) {
case JUST_WORKS:
case PASSKEY_CONFIRM:
break;
case PASSKEY_DISPLAY:
case PASSKEY_INPUT:
memcpy(r, &smp->passkey, sizeof(smp->passkey));
break;
default:
return BT_SMP_ERR_UNSPECIFIED;
}
/* calculate LTK and mackey */
if (smp_f5(smp->dhkey, smp->rrnd, smp->prnd,
&smp->chan.chan.conn->le.init_addr,
&smp->chan.chan.conn->le.resp_addr, smp->mackey,
smp->tk)) {
return BT_SMP_ERR_UNSPECIFIED;
}
/* calculate local DHKey check */
if (smp_f6(smp->mackey, smp->prnd, smp->rrnd, r, &smp->prsp[1],
&smp->chan.chan.conn->le.resp_addr,
&smp->chan.chan.conn->le.init_addr, e)) {
return BT_SMP_ERR_UNSPECIFIED;
}
/* calculate remote DHKey check */
if (smp_f6(smp->mackey, smp->rrnd, smp->prnd, r, &smp->preq[1],
&smp->chan.chan.conn->le.init_addr,
&smp->chan.chan.conn->le.resp_addr, re)) {
return BT_SMP_ERR_UNSPECIFIED;
}
/* compare received E with calculated remote */
if (memcmp(smp->e, re, 16)) {
return BT_SMP_ERR_DHKEY_CHECK_FAILED;
}
/* send local e */
sc_smp_send_dhkey_check(smp, e);
atomic_set_bit(smp->flags, SMP_FLAG_ENC_PENDING);
return 0;
}
#endif /* CONFIG_BLUETOOTH_PERIPHERAL */
static void bt_smp_dhkey_ready(const uint8_t *dhkey)
{
struct bt_smp *smp = NULL;
int i;
BT_DBG("%p", dhkey);
for (i = 0; i < ARRAY_SIZE(bt_smp_pool); i++) {
if (atomic_test_and_clear_bit(bt_smp_pool[i].flags,
SMP_FLAG_DHKEY_PENDING)) {
smp = &bt_smp_pool[i];
break;
}
}
if (!smp) {
return;
}
if (!dhkey) {
smp_error(smp, BT_SMP_ERR_DHKEY_CHECK_FAILED);
return;
}
memcpy(smp->dhkey, dhkey, 32);
/* wait for user passkey confirmation */
if (atomic_test_bit(smp->flags, SMP_FLAG_USER)) {
atomic_set_bit(smp->flags, SMP_FLAG_DHKEY_SEND);
return;
}
/* wait for remote DHKey Check */
if (atomic_test_bit(smp->flags, SMP_FLAG_DHCHECK_WAIT)) {
atomic_set_bit(smp->flags, SMP_FLAG_DHKEY_SEND);
return;
}
if (atomic_test_bit(smp->flags, SMP_FLAG_DHKEY_SEND)) {
uint8_t err;
#if defined(CONFIG_BLUETOOTH_CENTRAL)
if (smp->chan.chan.conn->role == BT_HCI_ROLE_MASTER) {
err = compute_and_send_master_dhcheck(smp);
if (err) {
smp_error(smp, err);
}
return;
}
#endif /* CONFIG_BLUETOOTH_CENTRAL */
#if defined(CONFIG_BLUETOOTH_PERIPHERAL)
err = compute_and_check_and_send_slave_dhcheck(smp);
if (err) {
smp_error(smp, err);
}
#endif /* CONFIG_BLUETOOTH_PERIPHERAL */
}
}
static uint8_t sc_smp_check_confirm(struct bt_smp *smp)
{
uint8_t cfm[16];
uint8_t r;
switch (smp->method) {
case PASSKEY_CONFIRM:
case JUST_WORKS:
r = 0;
break;
case PASSKEY_DISPLAY:
case PASSKEY_INPUT:
/*
* In the Passkey Entry protocol, the most significant
* bit of Z is set equal to one and the least
* significant bit is made up from one bit of the
* passkey e.g. if the passkey bit is 1, then Z = 0x81
* and if the passkey bit is 0, then Z = 0x80.
*/
r = (smp->passkey >> smp->passkey_round) & 0x01;
r |= 0x80;
break;
default:
return BT_SMP_ERR_UNSPECIFIED;
}
if (smp_f4(smp->pkey, sc_public_key, smp->rrnd, r, cfm)) {
return BT_SMP_ERR_UNSPECIFIED;
}
BT_DBG("pcnf %s cfm %s", bt_hex(smp->pcnf, 16), bt_hex(cfm, 16));
if (memcmp(smp->pcnf, cfm, 16)) {
return BT_SMP_ERR_CONFIRM_FAILED;
}
return 0;
}
static uint8_t smp_pairing_random(struct bt_smp *smp, struct net_buf *buf)
{
struct bt_smp_pairing_random *req = (void *)buf->data;
uint32_t passkey;
uint8_t err;
BT_DBG("");
memcpy(smp->rrnd, req->val, sizeof(smp->rrnd));
#if !defined(CONFIG_BLUETOOTH_SMP_SC_ONLY)
if (!atomic_test_bit(smp->flags, SMP_FLAG_SC)) {
return legacy_pairing_random(smp);
}
#endif /* !CONFIG_BLUETOOTH_SMP_SC_ONLY */
#if defined(CONFIG_BLUETOOTH_CENTRAL)
if (smp->chan.chan.conn->role == BT_HCI_ROLE_MASTER) {
err = sc_smp_check_confirm(smp);
if (err) {
return err;
}
switch (smp->method) {
case PASSKEY_CONFIRM:
/* compare passkey before calculating LTK */
if (smp_g2(sc_public_key, smp->pkey, smp->prnd,
smp->rrnd, &passkey)) {
return BT_SMP_ERR_UNSPECIFIED;
}
atomic_set_bit(smp->flags, SMP_FLAG_USER);
atomic_set_bit(smp->flags, SMP_FLAG_DHKEY_SEND);
bt_auth->passkey_confirm(smp->chan.chan.conn, passkey);
return 0;
case JUST_WORKS:
break;
case PASSKEY_DISPLAY:
case PASSKEY_INPUT:
smp->passkey_round++;
if (smp->passkey_round == 20) {
break;
}
if (bt_rand(smp->prnd, 16)) {
return BT_SMP_ERR_UNSPECIFIED;
}
atomic_set_bit(&smp->allowed_cmds,
BT_SMP_CMD_PAIRING_CONFIRM);
smp_send_pairing_confirm(smp);
return 0;
default:
return BT_SMP_ERR_UNSPECIFIED;
}
/* wait for DHKey being generated */
if (atomic_test_bit(smp->flags, SMP_FLAG_DHKEY_PENDING)) {
atomic_set_bit(smp->flags, SMP_FLAG_DHKEY_SEND);
return 0;
}
return compute_and_send_master_dhcheck(smp);
}
#endif /* CONFIG_BLUETOOTH_CENTRAL */
#if defined(CONFIG_BLUETOOTH_PERIPHERAL)
switch (smp->method) {
case PASSKEY_CONFIRM:
if (smp_g2(smp->pkey, sc_public_key, smp->rrnd, smp->prnd,
&passkey)) {
return BT_SMP_ERR_UNSPECIFIED;
}
atomic_set_bit(smp->flags, SMP_FLAG_USER);
bt_auth->passkey_confirm(smp->chan.chan.conn, passkey);
break;
case JUST_WORKS:
break;
case PASSKEY_DISPLAY:
case PASSKEY_INPUT:
err = sc_smp_check_confirm(smp);
if (err) {
return err;
}
atomic_set_bit(&smp->allowed_cmds,
BT_SMP_CMD_PAIRING_CONFIRM);
smp_send_pairing_random(smp);
smp->passkey_round++;
if (smp->passkey_round == 20) {
atomic_set_bit(&smp->allowed_cmds, BT_SMP_DHKEY_CHECK);
atomic_set_bit(smp->flags, SMP_FLAG_DHCHECK_WAIT);
return 0;
}
if (bt_rand(smp->prnd, 16)) {
return BT_SMP_ERR_UNSPECIFIED;
}
return 0;
default:
return BT_SMP_ERR_UNSPECIFIED;
}
atomic_set_bit(&smp->allowed_cmds, BT_SMP_DHKEY_CHECK);
atomic_set_bit(smp->flags, SMP_FLAG_DHCHECK_WAIT);
smp_send_pairing_random(smp);
#endif /* CONFIG_BLUETOOTH_PERIPHERAL */
return 0;
}
static uint8_t smp_pairing_failed(struct bt_smp *smp, struct net_buf *buf)
{
struct bt_conn *conn = smp->chan.chan.conn;
struct bt_smp_pairing_fail *req = (void *)buf->data;
BT_ERR("reason 0x%x", req->reason);
/* TODO report error
* for now this to avoid warning about unused variable when debugs are
* disabled
*/
ARG_UNUSED(req);
switch (smp->method) {
case PASSKEY_INPUT:
case PASSKEY_DISPLAY:
case PASSKEY_CONFIRM:
bt_auth->cancel(conn);
break;
default:
break;
}
/*
* Pairing Failed command may be sent at any time during the pairing,
* so if there are any keys distributed, shall be cleared.
*/
if (atomic_test_bit(smp->flags, SMP_FLAG_KEYS_DISTR) &&
smp->chan.chan.conn->keys) {
bt_keys_clear(smp->chan.chan.conn->keys, BT_KEYS_ALL);
}
smp_reset(smp);
/* return no error to avoid sending Pairing Failed in response */
return 0;
}
#if !defined(CONFIG_BLUETOOTH_SMP_SC_ONLY)
#endif
static uint8_t smp_ident_info(struct bt_smp *smp, struct net_buf *buf)
{
BT_DBG("");
if (atomic_test_bit(smp->flags, SMP_FLAG_BOND)) {
struct bt_smp_ident_info *req = (void *)buf->data;
struct bt_conn *conn = smp->chan.chan.conn;
struct bt_keys *keys;
keys = bt_keys_get_type(BT_KEYS_IRK, &conn->le.dst);
if (!keys) {
BT_ERR("Unable to get keys for %s",
bt_addr_le_str(&conn->le.dst));
return BT_SMP_ERR_UNSPECIFIED;
}
memcpy(keys->irk.val, req->irk, 16);
}
atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_IDENT_ADDR_INFO);
return 0;
}
static uint8_t smp_ident_addr_info(struct bt_smp *smp, struct net_buf *buf)
{
struct bt_conn *conn = smp->chan.chan.conn;
struct bt_smp_ident_addr_info *req = (void *)buf->data;
BT_DBG("identity %s", bt_addr_le_str(&req->addr));
if (!bt_addr_le_is_identity(&req->addr)) {
BT_ERR("Invalid identity %s for %s",
bt_addr_le_str(&req->addr), bt_addr_le_str(&conn->le.dst));
return BT_SMP_ERR_INVALID_PARAMS;
}
if (atomic_test_bit(smp->flags, SMP_FLAG_BOND)) {
const bt_addr_le_t *dst;
struct bt_keys *keys;
keys = bt_keys_get_type(BT_KEYS_IRK, &conn->le.dst);
if (!keys) {
BT_ERR("Unable to get keys for %s",
bt_addr_le_str(&conn->le.dst));
return BT_SMP_ERR_UNSPECIFIED;
}
/*
* We can't use conn->dst here as this might already contain
* identity address known from previous pairing. Since all keys
* are cleared on re-pairing we wouldn't store IRK distributed
* in new pairing.
*/
if (conn->role == BT_HCI_ROLE_MASTER) {
dst = &conn->le.resp_addr;
} else {
dst = &conn->le.init_addr;
}
if (bt_addr_le_is_rpa(dst)) {
/* always update last use RPA */
bt_addr_copy(&keys->irk.rpa, &dst->a);
/*
* Update connection address and notify about identity
* resolved only if connection wasn't already reported
* with identity address. This may happen if IRK was
* present before ie. due to re-pairing.
*/
if (!bt_addr_le_is_identity(&conn->le.dst)) {
bt_addr_le_copy(&keys->addr, &req->addr);
bt_addr_le_copy(&conn->le.dst, &req->addr);
bt_conn_identity_resolved(conn);
}
}
}
smp->remote_dist &= ~BT_SMP_DIST_ID_KEY;
if (smp->remote_dist & BT_SMP_DIST_SIGN) {
atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_SIGNING_INFO);
}
#if defined(CONFIG_BLUETOOTH_CENTRAL)
if (conn->role == BT_HCI_ROLE_MASTER && !smp->remote_dist) {
bt_smp_distribute_keys(smp);
}
#endif /* CONFIG_BLUETOOTH_CENTRAL */
/* if all keys were distributed, pairing is done */
if (!smp->local_dist && !smp->remote_dist) {
smp_reset(smp);
}
return 0;
}
#if defined(CONFIG_BLUETOOTH_SIGNING)
static uint8_t smp_signing_info(struct bt_smp *smp, struct net_buf *buf)
{
struct bt_conn *conn = smp->chan.chan.conn;
BT_DBG("");
if (atomic_test_bit(smp->flags, SMP_FLAG_BOND)) {
struct bt_smp_signing_info *req = (void *)buf->data;
struct bt_keys *keys;
keys = bt_keys_get_type(BT_KEYS_REMOTE_CSRK, &conn->le.dst);
if (!keys) {
BT_ERR("Unable to get keys for %s",
bt_addr_le_str(&conn->le.dst));
return BT_SMP_ERR_UNSPECIFIED;
}
memcpy(keys->remote_csrk.val, req->csrk,
sizeof(keys->remote_csrk.val));
}
smp->remote_dist &= ~BT_SMP_DIST_SIGN;
#if defined(CONFIG_BLUETOOTH_CENTRAL)
if (conn->role == BT_HCI_ROLE_MASTER && !smp->remote_dist) {
bt_smp_distribute_keys(smp);
}
#endif /* CONFIG_BLUETOOTH_CENTRAL */
/* if all keys were distributed, pairing is done */
if (!smp->local_dist && !smp->remote_dist) {
smp_reset(smp);
}
return 0;
}
#else
static uint8_t smp_signing_info(struct bt_smp *smp, struct net_buf *buf)
{
return BT_SMP_ERR_CMD_NOTSUPP;
}
#endif /* CONFIG_BLUETOOTH_SIGNING */
#if defined(CONFIG_BLUETOOTH_CENTRAL)
static uint8_t smp_security_request(struct bt_smp *smp, struct net_buf *buf)
{
struct bt_conn *conn = smp->chan.chan.conn;
struct bt_smp_security_request *req = (void *)buf->data;
uint8_t auth;
BT_DBG("");
if (sc_supported) {
auth = req->auth_req & BT_SMP_AUTH_MASK_SC;
} else {
auth = req->auth_req & BT_SMP_AUTH_MASK;
}
if (!conn->keys) {
conn->keys = bt_keys_find(BT_KEYS_LTK_P256, &conn->le.dst);
if (!conn->keys) {
conn->keys = bt_keys_find(BT_KEYS_LTK, &conn->le.dst);
}
}
if (!conn->keys) {
goto pair;
}
/* if MITM required key must be authenticated */
if ((auth & BT_SMP_AUTH_MITM) &&
!atomic_test_bit(conn->keys->flags, BT_KEYS_AUTHENTICATED)) {
if (get_io_capa() != BT_SMP_IO_NO_INPUT_OUTPUT) {
BT_INFO("New auth requirements: 0x%x, repairing",
auth);
goto pair;
}
BT_WARN("Unsupported auth requirements: 0x%x, repairing",
auth);
goto pair;
}
/* if LE SC required and no p256 key present reapair */
if ((auth & BT_SMP_AUTH_SC) && !(conn->keys->keys & BT_KEYS_LTK_P256)) {
BT_INFO("New auth requirements: 0x%x, repairing", auth);
goto pair;
}
if (bt_conn_le_start_encryption(conn, conn->keys->ltk.rand,
conn->keys->ltk.ediv,
conn->keys->ltk.val,
conn->keys->enc_size) < 0) {
return BT_SMP_ERR_UNSPECIFIED;
}
atomic_set_bit(smp->flags, SMP_FLAG_ENC_PENDING);
return 0;
pair:
if (bt_smp_send_pairing_req(conn) < 0) {
return BT_SMP_ERR_UNSPECIFIED;
}
atomic_set_bit(smp->flags, SMP_FLAG_SEC_REQ);
return 0;
}
#else
static uint8_t smp_security_request(struct bt_smp *smp, struct net_buf *buf)
{
return BT_SMP_ERR_CMD_NOTSUPP;
}
#endif /* CONFIG_BLUETOOTH_CENTRAL */
static uint8_t generate_dhkey(struct bt_smp *smp)
{
if (bt_dh_key_gen(smp->pkey, bt_smp_dhkey_ready)) {
return BT_SMP_ERR_UNSPECIFIED;
}
atomic_set_bit(smp->flags, SMP_FLAG_DHKEY_PENDING);
return 0;
}
static uint8_t display_passkey(struct bt_smp *smp)
{
if (bt_rand(&smp->passkey, sizeof(smp->passkey))) {
return BT_SMP_ERR_UNSPECIFIED;
}
smp->passkey %= 1000000;
smp->passkey_round = 0;
bt_auth->passkey_display(smp->chan.chan.conn, smp->passkey);
smp->passkey = sys_cpu_to_le32(smp->passkey);
return 0;
}
#if defined(CONFIG_BLUETOOTH_PERIPHERAL)
static uint8_t smp_public_key_slave(struct bt_smp *smp)
{
uint8_t err;
err = sc_send_public_key(smp);
if (err) {
return err;
}
switch (smp->method) {
case PASSKEY_CONFIRM:
case JUST_WORKS:
atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_PAIRING_RANDOM);
err = smp_send_pairing_confirm(smp);
if (err) {
return err;
}
break;
case PASSKEY_DISPLAY:
err = display_passkey(smp);
if (err) {
return err;
}
atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_PAIRING_CONFIRM);
break;
case PASSKEY_INPUT:
atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_PAIRING_CONFIRM);
atomic_set_bit(smp->flags, SMP_FLAG_USER);
bt_auth->passkey_entry(smp->chan.chan.conn);
break;
default:
return BT_SMP_ERR_UNSPECIFIED;
}
return generate_dhkey(smp);
}
#endif /* CONFIG_BLUETOOTH_PERIPHERAL */
static uint8_t smp_public_key(struct bt_smp *smp, struct net_buf *buf)
{
struct bt_smp_public_key *req = (void *)buf->data;
uint8_t err;
BT_DBG("");
memcpy(smp->pkey, req->x, 32);
memcpy(&smp->pkey[32], req->y, 32);
/* mark key as debug if remote is using it */
if (memcmp(smp->pkey, sc_debug_public_key, 64) == 0) {
BT_INFO("Remote is using Debug Public key");
atomic_set_bit(smp->flags, SMP_FLAG_SC_DEBUG_KEY);
}
#if defined(CONFIG_BLUETOOTH_CENTRAL)
if (smp->chan.chan.conn->role == BT_HCI_ROLE_MASTER) {
switch (smp->method) {
case PASSKEY_CONFIRM:
case JUST_WORKS:
atomic_set_bit(&smp->allowed_cmds,
BT_SMP_CMD_PAIRING_CONFIRM);
break;
case PASSKEY_DISPLAY:
err = display_passkey(smp);
if (err) {
return err;
}
atomic_set_bit(&smp->allowed_cmds,
BT_SMP_CMD_PAIRING_CONFIRM);
err = smp_send_pairing_confirm(smp);
if (err) {
return err;
}
break;
case PASSKEY_INPUT:
atomic_set_bit(smp->flags, SMP_FLAG_USER);
bt_auth->passkey_entry(smp->chan.chan.conn);
break;
default:
return BT_SMP_ERR_UNSPECIFIED;
}
return generate_dhkey(smp);
}
#endif /* CONFIG_BLUETOOTH_CENTRAL */
#if defined(CONFIG_BLUETOOTH_PERIPHERAL)
if (!sc_local_pkey_valid) {
atomic_set_bit(smp->flags, SMP_FLAG_PKEY_SEND);
return 0;
}
err = smp_public_key_slave(smp);
if (err) {
return err;
}
#endif /* CONFIG_BLUETOOTH_PERIPHERAL */
return 0;
}
static uint8_t smp_dhkey_check(struct bt_smp *smp, struct net_buf *buf)
{
struct bt_smp_dhkey_check *req = (void *)buf->data;
BT_DBG("");
#if defined(CONFIG_BLUETOOTH_CENTRAL)
if (smp->chan.chan.conn->role == BT_HCI_ROLE_MASTER) {
uint8_t e[16], r[16], enc_size;
memset(r, 0, sizeof(r));
switch (smp->method) {
case JUST_WORKS:
case PASSKEY_CONFIRM:
break;
case PASSKEY_DISPLAY:
case PASSKEY_INPUT:
memcpy(r, &smp->passkey, sizeof(smp->passkey));
break;
default:
return BT_SMP_ERR_UNSPECIFIED;
}
/* calculate remote DHKey check for comparison */
if (smp_f6(smp->mackey, smp->rrnd, smp->prnd, r, &smp->prsp[1],
&smp->chan.chan.conn->le.resp_addr,
&smp->chan.chan.conn->le.init_addr, e)) {
return BT_SMP_ERR_UNSPECIFIED;
}
if (memcmp(e, req->e, 16)) {
return BT_SMP_ERR_DHKEY_CHECK_FAILED;
}
enc_size = get_encryption_key_size(smp);
if (bt_conn_le_start_encryption(smp->chan.chan.conn, 0, 0,
smp->tk, enc_size) < 0) {
return BT_SMP_ERR_UNSPECIFIED;
}
atomic_set_bit(smp->flags, SMP_FLAG_ENC_PENDING);
return 0;
}
#endif /* CONFIG_BLUETOOTH_CENTRAL */
#if defined(CONFIG_BLUETOOTH_PERIPHERAL)
if (smp->chan.chan.conn->role == BT_HCI_ROLE_SLAVE) {
atomic_clear_bit(smp->flags, SMP_FLAG_DHCHECK_WAIT);
memcpy(smp->e, req->e, sizeof(smp->e));
/* wait for DHKey being generated */
if (atomic_test_bit(smp->flags, SMP_FLAG_DHKEY_PENDING)) {
atomic_set_bit(smp->flags, SMP_FLAG_DHKEY_SEND);
return 0;
}
/* waiting for user to confirm passkey */
if (atomic_test_bit(smp->flags, SMP_FLAG_USER)) {
atomic_set_bit(smp->flags, SMP_FLAG_DHKEY_SEND);
return 0;
}
return compute_and_check_and_send_slave_dhcheck(smp);
}
#endif /* CONFIG_BLUETOOTH_PERIPHERAL */
return 0;
}
static const struct {
uint8_t (*func)(struct bt_smp *smp, struct net_buf *buf);
uint8_t expect_len;
} handlers[] = {
{ }, /* No op-code defined for 0x00 */
{ smp_pairing_req, sizeof(struct bt_smp_pairing) },
{ smp_pairing_rsp, sizeof(struct bt_smp_pairing) },
{ smp_pairing_confirm, sizeof(struct bt_smp_pairing_confirm) },
{ smp_pairing_random, sizeof(struct bt_smp_pairing_random) },
{ smp_pairing_failed, sizeof(struct bt_smp_pairing_fail) },
{ smp_encrypt_info, sizeof(struct bt_smp_encrypt_info) },
{ smp_master_ident, sizeof(struct bt_smp_master_ident) },
{ smp_ident_info, sizeof(struct bt_smp_ident_info) },
{ smp_ident_addr_info, sizeof(struct bt_smp_ident_addr_info) },
{ smp_signing_info, sizeof(struct bt_smp_signing_info) },
{ smp_security_request, sizeof(struct bt_smp_security_request) },
{ smp_public_key, sizeof(struct bt_smp_public_key) },
{ smp_dhkey_check, sizeof(struct bt_smp_dhkey_check) },
};
static void bt_smp_recv(struct bt_l2cap_chan *chan, struct net_buf *buf)
{
struct bt_smp *smp = CONTAINER_OF(chan, struct bt_smp, chan);
struct bt_smp_hdr *hdr = (void *)buf->data;
uint8_t err;
if (buf->len < sizeof(*hdr)) {
BT_ERR("Too small SMP PDU received");