<commit_before>/**
 * @file smp.c
 * Security Manager Protocol implementation
 */

/*
 * Copyright (c) 2015 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 <bluetooth/log.h>
#include <bluetooth/hci.h>
#include <bluetooth/bluetooth.h>

#if defined(CONFIG_TINYCRYPT_AES)
#include <tinycrypt/aes.h>
#include <tinycrypt/utils.h>
#endif

#include "hci_core.h"
#include "keys.h"
#include "conn_internal.h"
#include "l2cap_internal.h"
#include "smp.h"
#include "stack.h"

#define SMP_TIMEOUT (30 * sys_clock_ticks_per_sec)

#if defined(CONFIG_BLUETOOTH_SIGNING)
#define RECV_KEYS (BT_SMP_DIST_ID_KEY | BT_SMP_DIST_ENC_KEY | BT_SMP_DIST_SIGN)
#define SEND_KEYS (BT_SMP_DIST_ENC_KEY | BT_SMP_DIST_SIGN)
#else
#define RECV_KEYS (BT_SMP_DIST_ID_KEY | BT_SMP_DIST_ENC_KEY)
#define SEND_KEYS (BT_SMP_DIST_ENC_KEY)
#endif /* CONFIG_BLUETOOTH_SIGNING */

enum pairing_method {
	JUST_WORKS,		/* JustWorks pairing */
	PASSKEY_INPUT,		/* Passkey Entry input */
	PASSKEY_DISPLAY,	/* Passkey Entry display */
	PASSKEY_ROLE,		/* Passkey Entry depends on role */
} ;

enum {
	SMP_FLAG_TK_VALID,	/* if TK values is valid */
	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_PAIRING,	/* if pairing is in progress */
	SMP_FLAG_TIMEOUT,	/* if SMP timeout occurred */
};

/* SMP channel specific context */
struct bt_smp {
	/* The channel this context is associated with */
	struct bt_l2cap_chan	chan;

	/* SMP Timeout fiber handle */
	void			*timeout;

	/* Commands that remote is allowed to send */
	atomic_t		allowed_cmds;

	/* Flags for SMP state machine */
	atomic_t		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];

	/* Local key distribution */
	uint8_t			local_dist;

	/* Remote key distribution */
	uint8_t			remote_dist;

	/* stack for timeout fiber */
	BT_STACK(stack, 128);
};

/* based on table 2.8 Core Spec 2.3.5.1 Vol. 3 Part H */
static const uint8_t gen_method[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 },
};

static struct bt_smp bt_smp_pool[CONFIG_BLUETOOTH_MAX_CONN];
static const struct bt_auth_cb *auth_cb;
static uint8_t bt_smp_io_capa = 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;
	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[remote_io][bt_smp_io_capa];

	/* if both sides have KeyboardDisplay capabilities, initiator displays
	 * and responder inputs
	 */
	if (method == PASSKEY_ROLE) {
		if (smp->chan.conn->role == BT_HCI_ROLE_MASTER) {
			method = PASSKEY_DISPLAY;
		} else {
			method = PASSKEY_INPUT;
		}
	}

	return method;
}

#if defined(CONFIG_BLUETOOTH_DEBUG_SMP)
/* Helper for printk parameters to convert from binary to hex.
 * We declare multiple buffers so the helper can be used multiple times
 * in a single printk call.
 */
static const char *h(const void *buf, size_t len)
{
	static const char hex[] = "0123456789abcdef";
	static char hexbufs[4][129];
	static uint8_t curbuf;
	const uint8_t *b = buf;
	char *str;
	int i;

	str = hexbufs[curbuf++];
	curbuf %= ARRAY_SIZE(hexbufs);

	len = min(len, (sizeof(hexbufs[0]) - 1) / 2);

	for (i = 0; i < len; i++) {
		str[i * 2]     = hex[b[i] >> 4];
		str[i * 2 + 1] = hex[b[i] & 0xf];
	}

	str[i * 2] = '\0';

	return str;
}
#else
#undef BT_DBG
#define BT_DBG(fmt, ...)
#endif

typedef struct {
	uint64_t a;
	uint64_t b;
} uint128_t;

static void xor_128(const uint128_t *p, const uint128_t *q, uint128_t *r)
{
	r->a = p->a ^ q->a;
	r->b = p->b ^ q->b;
}

#if defined(CONFIG_TINYCRYPT_AES) || defined(CONFIG_BLUETOOTH_SIGNING)
/* swap octets for LE encrypt */
static void swap_buf(const uint8_t *src, uint8_t *dst, 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;
	}
}
#endif /* CONFIG_TINYCRYPT_AES || CONFIG_BLUETOOTH_SIGNING */

#if defined(CONFIG_TINYCRYPT_AES)
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\n", h(key, 16), h(plaintext, 16));

	swap_buf(key, tmp, 16);

	if (tc_aes128_set_encrypt_key(&s, tmp) == TC_FAIL) {
		return -EINVAL;
	}

	swap_buf(plaintext, tmp, 16);

	if (tc_aes_encrypt(enc_data, tmp, &s) == TC_FAIL) {
		return -EINVAL;
	}

	swap_in_place(enc_data, 16);

	BT_DBG("enc_data %s\n", h(enc_data, 16));

	return 0;
}
#else
static int le_encrypt(const uint8_t key[16], const uint8_t plaintext[16],
		      uint8_t enc_data[16])
{
	struct bt_hci_cp_le_encrypt *cp;
	struct bt_hci_rp_le_encrypt *rp;
	struct bt_buf *buf, *rsp;
	int err;

	BT_DBG("key %s plaintext %s\n", h(key, 16), h(plaintext, 16));

	buf = bt_hci_cmd_create(BT_HCI_OP_LE_ENCRYPT, sizeof(*cp));
	if (!buf) {
		return -ENOBUFS;
	}

	cp = bt_buf_add(buf, sizeof(*cp));
	memcpy(cp->key, key, sizeof(cp->key));
	memcpy(cp->plaintext, plaintext, sizeof(cp->plaintext));

	err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_ENCRYPT, buf, &rsp);
	if (err) {
		return err;
	}

	rp = (void *)rsp->data;
	memcpy(enc_data, rp->enc_data, sizeof(rp->enc_data));
	bt_buf_put(rsp);

	BT_DBG("enc_data %s\n", h(enc_data, 16));

	return 0;
}
#endif

static int le_rand(void *buf, size_t len)
{
	uint8_t *ptr = buf;

	while (len > 0) {
		struct bt_hci_rp_le_rand *rp;
		struct bt_buf *rsp;
		size_t copy;
		int err;

		err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_RAND, NULL, &rsp);
		if (err) {
			BT_ERR("HCI_LE_Random failed (%d)\n", err);
			return err;
		}

		rp = (void *)rsp->data;
		copy = min(len, sizeof(rp->rand));
		memcpy(ptr, rp->rand, copy);
		bt_buf_put(rsp);

		len -= copy;
		ptr += copy;
	}

	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\n, r %s", h(irk, 16), h(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;
}

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\n", h(k, 16), h(r, 16));
	BT_DBG("ia %s ra %s\n", bt_addr_le_str(ia), bt_addr_le_str(ra));
	BT_DBG("preq %s pres %s\n", h(preq, 7), h(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\n", h(p1, 16));

	/* c1 = e(k, e(k, r XOR p1) XOR p2) */

	/* Using enc_data as temporary output buffer */
	xor_128((uint128_t *)r, (uint128_t *)p1, (uint128_t *)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->val, 6);
	memcpy(p2 + 6, ia->val, 6);
	memset(p2 + 12, 0, 4);

	BT_DBG("p2 %s\n", h(p2, 16));

	xor_128((uint128_t *)enc_data, (uint128_t *)p2, (uint128_t *)enc_data);

	return le_encrypt(k, enc_data, enc_data);
}

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 void smp_reset(struct bt_smp *smp)
{
	struct bt_conn *conn = smp->chan.conn;

	if (smp->timeout) {
		fiber_fiber_delayed_start_cancel(smp->timeout);
		smp->timeout = NULL;

		stack_analyze("smp timeout stack", smp->stack,
			      sizeof(smp->stack));
	}

	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;
	}

	switch (conn->role) {
#if defined(CONFIG_BLUETOOTH_CENTRAL)
	case BT_HCI_ROLE_MASTER:
		atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_SECURITY_REQUEST);
		break;
#endif /* CONFIG_BLUETOOTH_CENTRAL */

#if defined(CONFIG_BLUETOOTH_PERIPHERAL)
	case BT_HCI_ROLE_SLAVE:
		atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_PAIRING_REQ);
		break;
#endif /* CONFIG_BLUETOOTH_PERIPHERAL */
	default:
		break;
	}
}

static void smp_timeout(int arg1, int arg2)
{
	struct bt_smp *smp = (struct bt_smp *)arg1;
	ARG_UNUSED(arg2);

	BT_ERR("SMP Timeout\n");

	smp->timeout = NULL;

	smp_reset(smp);

	atomic_set_bit(&smp->flags, SMP_FLAG_TIMEOUT);
}

static void smp_restart_timer(struct bt_smp *smp)
{
	if (smp->timeout) {
		fiber_fiber_delayed_start_cancel(smp->timeout);
	}

	smp->timeout = fiber_delayed_start(smp->stack, sizeof(smp->stack),
					   smp_timeout, (int) smp, 0, 7, 0,
					   SMP_TIMEOUT);
}

struct bt_buf *bt_smp_create_pdu(struct bt_conn *conn, uint8_t op, size_t len)
{
	struct bt_smp_hdr *hdr;
	struct bt_buf *buf;

	buf = bt_l2cap_create_pdu(conn);
	if (!buf) {
		return NULL;
	}

	hdr = bt_buf_add(buf, sizeof(*hdr));
	hdr->code = op;

	return buf;
}

static void send_err_rsp(struct bt_conn *conn, uint8_t reason)
{
	struct bt_smp_pairing_fail *rsp;
	struct bt_buf *buf;

	buf = bt_smp_create_pdu(conn, BT_SMP_CMD_PAIRING_FAIL, sizeof(*rsp));
	if (!buf) {
		return;
	}

	rsp = bt_buf_add(buf, sizeof(*rsp));
	rsp->reason = reason;

	bt_l2cap_send(conn, BT_L2CAP_CID_SMP, buf);
}

static int smp_init(struct bt_smp *smp)
{
	/* Initialize SMP context */
	memset(smp + sizeof(smp->chan), 0, sizeof(*smp) - sizeof(smp->chan));

	/* Generate local random number */
	if (le_rand(smp->prnd, 16)) {
		return BT_SMP_ERR_UNSPECIFIED;
	}

	BT_DBG("prnd %s\n", h(smp->prnd, 16));

	atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_PAIRING_FAIL);

	return 0;
}

static uint8_t get_auth(uint8_t auth)
{
	auth &= BT_SMP_AUTH_MASK;

	if (bt_smp_io_capa == BT_SMP_IO_NO_INPUT_OUTPUT) {
		auth &= ~(BT_SMP_AUTH_MITM);
	} else {
		auth |= BT_SMP_AUTH_MITM;
	}

	return auth;
}

static uint8_t smp_request_tk(struct bt_smp *smp, uint8_t remote_io)
{
	struct bt_conn *conn = smp->chan.conn;
	struct bt_keys *keys;
	uint32_t passkey;

	smp->method = get_pair_method(smp, remote_io);

	/* 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->dst);
	if (keys && keys->type == BT_KEYS_AUTHENTICATED &&
	    smp->method == JUST_WORKS) {
		BT_ERR("JustWorks failed, authenticated keys present\n");
		return BT_SMP_ERR_UNSPECIFIED;
	}

	switch (smp->method) {
	case PASSKEY_DISPLAY:
		if (le_rand(&passkey, sizeof(passkey))) {
			return BT_SMP_ERR_UNSPECIFIED;
		}

		passkey %= 1<PASSWORD>;

		auth_cb->passkey_display(conn, passkey);

		passkey = sys_cpu_to_le32(passkey);
		memcpy(smp->tk, &passkey, sizeof(passkey));
		atomic_set_bit(&smp->flags, SMP_FLAG_TK_VALID);

		break;
	case PASSKEY_INPUT:
		auth_cb->passkey_entry(conn);
		break;
	case JUST_WORKS:
		atomic_set_bit(&smp->flags, SMP_FLAG_TK_VALID);
		break;
	default:
		BT_ERR("Unknown pairing method (%u)\n", smp->method);
		return BT_SMP_ERR_UNSPECIFIED;
	}

	return 0;
}

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 bt_smp_io_capa != BT_SMP_IO_NO_INPUT_OUTPUT;
	case BT_SECURITY_FIPS:
	default:
		return false;
	}
}

static struct bt_smp *smp_chan_get(struct bt_conn *conn)
{
	struct bt_l2cap_chan *chan;

	chan = bt_l2cap_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 bt_buf *req_buf;

	BT_DBG("\n");
	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 = bt_smp_create_pdu(conn, BT_SMP_CMD_SECURITY_REQUEST,
				    sizeof(*req));
	if (!req_buf) {
		return -ENOBUFS;
	}

	req = bt_buf_add(req_buf, sizeof(*req));

	req->auth_req = BT_SMP_AUTH_BONDING;

	if (conn->required_sec_level >= BT_SECURITY_HIGH) {
		req->auth_req |= BT_SMP_AUTH_MITM;
	}

	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 bt_buf *buf)
{
	struct bt_conn *conn = smp->chan.conn;
	struct bt_smp_pairing *req = (void *)buf->data;
	struct bt_smp_pairing *rsp;
	struct bt_buf *rsp_buf;
	int ret;

	BT_DBG("\n");

	if ((req->max_key_size > BT_SMP_MAX_ENC_KEY_SIZE) ||<commit_msg>Bluetooth: SMP: Use best effort for MITM in Security Request<commit_after>/**
 * @file smp.c
 * Security Manager Protocol implementation
 */

/*
 * Copyright (c) 2015 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 <bluetooth/log.h>
#include <bluetooth/hci.h>
#include <bluetooth/bluetooth.h>

#if defined(CONFIG_TINYCRYPT_AES)
#include <tinycrypt/aes.h>
#include <tinycrypt/utils.h>
#endif

#include "hci_core.h"
#include "keys.h"
#include "conn_internal.h"
#include "l2cap_internal.h"
#include "smp.h"
#include "stack.h"

#define SMP_TIMEOUT (30 * sys_clock_ticks_per_sec)

#if defined(CONFIG_BLUETOOTH_SIGNING)
#define RECV_KEYS (BT_SMP_DIST_ID_KEY | BT_SMP_DIST_ENC_KEY | BT_SMP_DIST_SIGN)
#define SEND_KEYS (BT_SMP_DIST_ENC_KEY | BT_SMP_DIST_SIGN)
#else
#define RECV_KEYS (BT_SMP_DIST_ID_KEY | BT_SMP_DIST_ENC_KEY)
#define SEND_KEYS (BT_SMP_DIST_ENC_KEY)
#endif /* CONFIG_BLUETOOTH_SIGNING */

enum pairing_method {
	JUST_WORKS,		/* JustWorks pairing */
	PASSKEY_INPUT,		/* Passkey Entry input */
	PASSKEY_DISPLAY,	/* Passkey Entry display */
	PASSKEY_ROLE,		/* Passkey Entry depends on role */
} ;

enum {
	SMP_FLAG_TK_VALID,	/* if TK values is valid */
	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_PAIRING,	/* if pairing is in progress */
	SMP_FLAG_TIMEOUT,	/* if SMP timeout occurred */
};

/* SMP channel specific context */
struct bt_smp {
	/* The channel this context is associated with */
	struct bt_l2cap_chan	chan;

	/* SMP Timeout fiber handle */
	void			*timeout;

	/* Commands that remote is allowed to send */
	atomic_t		allowed_cmds;

	/* Flags for SMP state machine */
	atomic_t		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];

	/* Local key distribution */
	uint8_t			local_dist;

	/* Remote key distribution */
	uint8_t			remote_dist;

	/* stack for timeout fiber */
	BT_STACK(stack, 128);
};

/* based on table 2.8 Core Spec 2.3.5.1 Vol. 3 Part H */
static const uint8_t gen_method[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 },
};

static struct bt_smp bt_smp_pool[CONFIG_BLUETOOTH_MAX_CONN];
static const struct bt_auth_cb *auth_cb;
static uint8_t bt_smp_io_capa = 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;
	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[remote_io][bt_smp_io_capa];

	/* if both sides have KeyboardDisplay capabilities, initiator displays
	 * and responder inputs
	 */
	if (method == PASSKEY_ROLE) {
		if (smp->chan.conn->role == BT_HCI_ROLE_MASTER) {
			method = PASSKEY_DISPLAY;
		} else {
			method = PASSKEY_INPUT;
		}
	}

	return method;
}

#if defined(CONFIG_BLUETOOTH_DEBUG_SMP)
/* Helper for printk parameters to convert from binary to hex.
 * We declare multiple buffers so the helper can be used multiple times
 * in a single printk call.
 */
static const char *h(const void *buf, size_t len)
{
	static const char hex[] = "0123456789abcdef";
	static char hexbufs[4][129];
	static uint8_t curbuf;
	const uint8_t *b = buf;
	char *str;
	int i;

	str = hexbufs[curbuf++];
	curbuf %= ARRAY_SIZE(hexbufs);

	len = min(len, (sizeof(hexbufs[0]) - 1) / 2);

	for (i = 0; i < len; i++) {
		str[i * 2]     = hex[b[i] >> 4];
		str[i * 2 + 1] = hex[b[i] & 0xf];
	}

	str[i * 2] = '\0';

	return str;
}
#else
#undef BT_DBG
#define BT_DBG(fmt, ...)
#endif

typedef struct {
	uint64_t a;
	uint64_t b;
} uint128_t;

static void xor_128(const uint128_t *p, const uint128_t *q, uint128_t *r)
{
	r->a = p->a ^ q->a;
	r->b = p->b ^ q->b;
}

#if defined(CONFIG_TINYCRYPT_AES) || defined(CONFIG_BLUETOOTH_SIGNING)
/* swap octets for LE encrypt */
static void swap_buf(const uint8_t *src, uint8_t *dst, 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;
	}
}
#endif /* CONFIG_TINYCRYPT_AES || CONFIG_BLUETOOTH_SIGNING */

#if defined(CONFIG_TINYCRYPT_AES)
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\n", h(key, 16), h(plaintext, 16));

	swap_buf(key, tmp, 16);

	if (tc_aes128_set_encrypt_key(&s, tmp) == TC_FAIL) {
		return -EINVAL;
	}

	swap_buf(plaintext, tmp, 16);

	if (tc_aes_encrypt(enc_data, tmp, &s) == TC_FAIL) {
		return -EINVAL;
	}

	swap_in_place(enc_data, 16);

	BT_DBG("enc_data %s\n", h(enc_data, 16));

	return 0;
}
#else
static int le_encrypt(const uint8_t key[16], const uint8_t plaintext[16],
		      uint8_t enc_data[16])
{
	struct bt_hci_cp_le_encrypt *cp;
	struct bt_hci_rp_le_encrypt *rp;
	struct bt_buf *buf, *rsp;
	int err;

	BT_DBG("key %s plaintext %s\n", h(key, 16), h(plaintext, 16));

	buf = bt_hci_cmd_create(BT_HCI_OP_LE_ENCRYPT, sizeof(*cp));
	if (!buf) {
		return -ENOBUFS;
	}

	cp = bt_buf_add(buf, sizeof(*cp));
	memcpy(cp->key, key, sizeof(cp->key));
	memcpy(cp->plaintext, plaintext, sizeof(cp->plaintext));

	err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_ENCRYPT, buf, &rsp);
	if (err) {
		return err;
	}

	rp = (void *)rsp->data;
	memcpy(enc_data, rp->enc_data, sizeof(rp->enc_data));
	bt_buf_put(rsp);

	BT_DBG("enc_data %s\n", h(enc_data, 16));

	return 0;
}
#endif

static int le_rand(void *buf, size_t len)
{
	uint8_t *ptr = buf;

	while (len > 0) {
		struct bt_hci_rp_le_rand *rp;
		struct bt_buf *rsp;
		size_t copy;
		int err;

		err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_RAND, NULL, &rsp);
		if (err) {
			BT_ERR("HCI_LE_Random failed (%d)\n", err);
			return err;
		}

		rp = (void *)rsp->data;
		copy = min(len, sizeof(rp->rand));
		memcpy(ptr, rp->rand, copy);
		bt_buf_put(rsp);

		len -= copy;
		ptr += copy;
	}

	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\n, r %s", h(irk, 16), h(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;
}

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\n", h(k, 16), h(r, 16));
	BT_DBG("ia %s ra %s\n", bt_addr_le_str(ia), bt_addr_le_str(ra));
	BT_DBG("preq %s pres %s\n", h(preq, 7), h(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\n", h(p1, 16));

	/* c1 = e(k, e(k, r XOR p1) XOR p2) */

	/* Using enc_data as temporary output buffer */
	xor_128((uint128_t *)r, (uint128_t *)p1, (uint128_t *)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->val, 6);
	memcpy(p2 + 6, ia->val, 6);
	memset(p2 + 12, 0, 4);

	BT_DBG("p2 %s\n", h(p2, 16));

	xor_128((uint128_t *)enc_data, (uint128_t *)p2, (uint128_t *)enc_data);

	return le_encrypt(k, enc_data, enc_data);
}

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 void smp_reset(struct bt_smp *smp)
{
	struct bt_conn *conn = smp->chan.conn;

	if (smp->timeout) {
		fiber_fiber_delayed_start_cancel(smp->timeout);
		smp->timeout = NULL;

		stack_analyze("smp timeout stack", smp->stack,
			      sizeof(smp->stack));
	}

	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;
	}

	switch (conn->role) {
#if defined(CONFIG_BLUETOOTH_CENTRAL)
	case BT_HCI_ROLE_MASTER:
		atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_SECURITY_REQUEST);
		break;
#endif /* CONFIG_BLUETOOTH_CENTRAL */

#if defined(CONFIG_BLUETOOTH_PERIPHERAL)
	case BT_HCI_ROLE_SLAVE:
		atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_PAIRING_REQ);
		break;
#endif /* CONFIG_BLUETOOTH_PERIPHERAL */
	default:
		break;
	}
}

static void smp_timeout(int arg1, int arg2)
{
	struct bt_smp *smp = (struct bt_smp *)arg1;
	ARG_UNUSED(arg2);

	BT_ERR("SMP Timeout\n");

	smp->timeout = NULL;

	smp_reset(smp);

	atomic_set_bit(&smp->flags, SMP_FLAG_TIMEOUT);
}

static void smp_restart_timer(struct bt_smp *smp)
{
	if (smp->timeout) {
		fiber_fiber_delayed_start_cancel(smp->timeout);
	}

	smp->timeout = fiber_delayed_start(smp->stack, sizeof(smp->stack),
					   smp_timeout, (int) smp, 0, 7, 0,
					   SMP_TIMEOUT);
}

struct bt_buf *bt_smp_create_pdu(struct bt_conn *conn, uint8_t op, size_t len)
{
	struct bt_smp_hdr *hdr;
	struct bt_buf *buf;

	buf = bt_l2cap_create_pdu(conn);
	if (!buf) {
		return NULL;
	}

	hdr = bt_buf_add(buf, sizeof(*hdr));
	hdr->code = op;

	return buf;
}

static void send_err_rsp(struct bt_conn *conn, uint8_t reason)
{
	struct bt_smp_pairing_fail *rsp;
	struct bt_buf *buf;

	buf = bt_smp_create_pdu(conn, BT_SMP_CMD_PAIRING_FAIL, sizeof(*rsp));
	if (!buf) {
		return;
	}

	rsp = bt_buf_add(buf, sizeof(*rsp));
	rsp->reason = reason;

	bt_l2cap_send(conn, BT_L2CAP_CID_SMP, buf);
}

static int smp_init(struct bt_smp *smp)
{
	/* Initialize SMP context */
	memset(smp + sizeof(smp->chan), 0, sizeof(*smp) - sizeof(smp->chan));

	/* Generate local random number */
	if (le_rand(smp->prnd, 16)) {
		return BT_SMP_ERR_UNSPECIFIED;
	}

	BT_DBG("prnd %s\n", h(smp->prnd, 16));

	atomic_set_bit(&smp->allowed_cmds, BT_SMP_CMD_PAIRING_FAIL);

	return 0;
}

static uint8_t get_auth(uint8_t auth)
{
	auth &= BT_SMP_AUTH_MASK;

	if (bt_smp_io_capa == BT_SMP_IO_NO_INPUT_OUTPUT) {
		auth &= ~(BT_SMP_AUTH_MITM);
	} else {
		auth |= BT_SMP_AUTH_MITM;
	}

	return auth;
}

static uint8_t smp_request_tk(struct bt_smp *smp, uint8_t remote_io)
{
	struct bt_conn *conn = smp->chan.conn;
	struct bt_keys *keys;
	uint32_t passkey;

	smp->method = get_pair_method(smp, remote_io);

	/* 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->dst);
	if (keys && keys->type == BT_KEYS_AUTHENTICATED &&
	    smp->method == JUST_WORKS) {
		BT_ERR("JustWorks failed, authenticated keys present\n");
		return BT_SMP_ERR_UNSPECIFIED;
	}

	switch (smp->method) {
	case PASSKEY_DISPLAY:
		if (le_rand(&passkey, sizeof(passkey))) {
			return BT_SMP_ERR_UNSPECIFIED;
		}

		passkey %= 1000000;

		auth_cb->passkey_display(conn, passkey);

		passkey = sys_cpu_to_le32(passkey);
		memcpy(smp->tk, &passkey, sizeof(passkey));
		atomic_set_bit(&smp->flags, SMP_FLAG_TK_VALID);

		break;
	case PASSKEY_INPUT:
		auth_cb->passkey_entry(conn);
		break;
	case JUST_WORKS:
		atomic_set_bit(&smp->flags, SMP_FLAG_TK_VALID);
		break;
	default:
		BT_ERR("Unknown pairing method (%u)\n", smp->method);
		return BT_SMP_ERR_UNSPECIFIED;
	}

	return 0;
}

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 bt_smp_io_capa != BT_SMP_IO_NO_INPUT_OUTPUT;
	case BT_SECURITY_FIPS:
	default:
		return false;
	}
}

static struct bt_smp *smp_chan_get(struct bt_conn *conn)
{
	struct bt_l2cap_chan *chan;

	chan = bt_l2cap_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 bt_buf *req_buf;

	BT_DBG("\n");
	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 = bt_smp_create_pdu(conn, BT_SMP_CMD_SECURITY_REQUEST,
				    sizeof(*req));
	if (!req_buf) {
		return -ENOBUFS;
	}

	req = bt_buf_add(req_buf, sizeof(*req));
	req->auth_req = get_auth(BT_SMP_AUTH_BONDING);

	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 bt_buf *buf)
{
	struct bt_conn *conn = smp->chan.conn;
	struct bt_smp_pairing *req = (void *)buf->data;
	struct bt_smp_pairing *rsp;
	struct bt_buf *rsp_buf;
	int ret;

	BT_DBG("\n");

	if ((req->max_key_size > BT_SMP_MAX_ENC_KEY_SIZE) ||
{'fraction_non_alphanumeric': 0.1349423490326363, 'fraction_numerical': 0.022539248257442513, 'mean_word_length': 4.217162276975361, 'pattern_counts': {'":': 0, '<': 36, '<?xml version=': 0, '>': 195, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 2, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 3, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6844703', 'n_tokens_mistral': 13857, 'n_tokens_neox': 12890, 'n_words': 3058}