mirror of
https://github.com/zephyrproject-rtos/zephyr
synced 2025-08-28 22:35:56 +00:00
Command Status, Command Complete and Number of Complete Packets are used for driving TX fibers (HCI and connection). Missing any of those event will lead to HCI traffic stall. Priority pool will be used when critical HCI event is received or normal pool runs out of buffers. The difference with priority pool is that buffer from it must not be passed to RX fiber and must be freed from bt_recv. If driver knows HCI event opcode before requesting buffer for event it may hint HCI core about preferred pool selection. Change-Id: Iad14724945bb59721c5ffb6b62d5a8a3e3f70be7 Signed-off-by: Szymon Janc <ext.szymon.janc@tieto.com>
250 lines
5.9 KiB
C
250 lines
5.9 KiB
C
/**
|
|
* @file hci_ecc.c
|
|
* HCI ECC emulation
|
|
*/
|
|
|
|
/*
|
|
* Copyright (c) 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 <zephyr.h>
|
|
#include <atomic.h>
|
|
#include <microkernel/task.h>
|
|
#include <misc/byteorder.h>
|
|
#include <tinycrypt/utils.h>
|
|
#include <tinycrypt/ecc.h>
|
|
#include <tinycrypt/ecc_dh.h>
|
|
#include <bluetooth/bluetooth.h>
|
|
#include <bluetooth/log.h>
|
|
#include <bluetooth/hci.h>
|
|
#include <bluetooth/driver.h>
|
|
#include "hci_core.h"
|
|
|
|
#if !defined(CONFIG_BLUETOOTH_DEBUG_HCI_CORE)
|
|
#undef BT_DBG
|
|
#define BT_DBG(fmt, ...)
|
|
#endif
|
|
|
|
/* based on Core Specification 4.2 Vol 3. Part H 2.3.5.6.1 */
|
|
static const uint32_t debug_private_key[8] = {
|
|
0xcd3c1abd, 0x5899b8a6, 0xeb40b799, 0x4aff607b, 0xd2103f50, 0x74c9b3e3,
|
|
0xa3c55f38, 0x3f49f6d4
|
|
};
|
|
|
|
static struct nano_fifo ecc_queue;
|
|
static int (*drv_send)(struct net_buf *buf);
|
|
static uint32_t private_key[8];
|
|
|
|
static void send_cmd_status(uint16_t opcode, uint8_t status)
|
|
{
|
|
struct bt_hci_evt_cmd_status *evt;
|
|
struct bt_hci_evt_hdr *hdr;
|
|
struct net_buf *buf;
|
|
|
|
BT_DBG("opcode %x status %x", opcode, status);
|
|
|
|
buf = bt_buf_get_evt(BT_HCI_EVT_CMD_STATUS);
|
|
if (!buf) {
|
|
BT_ERR("No available event buffers!");
|
|
return;
|
|
}
|
|
|
|
hdr = net_buf_add(buf, sizeof(*hdr));
|
|
hdr->evt = BT_HCI_EVT_CMD_STATUS;
|
|
hdr->len = sizeof(*evt);
|
|
|
|
evt = net_buf_add(buf, sizeof(*evt));
|
|
evt->ncmd = 1;
|
|
evt->opcode = sys_cpu_to_le16(opcode);
|
|
evt->status = status;
|
|
|
|
bt_recv(buf);
|
|
}
|
|
|
|
static void emulate_le_p256_public_key_cmd(struct net_buf *buf)
|
|
{
|
|
struct bt_hci_evt_le_p256_public_key_complete *evt;
|
|
struct bt_hci_evt_le_meta_event *meta;
|
|
struct bt_hci_evt_hdr *hdr;
|
|
EccPoint pkey;
|
|
|
|
BT_DBG();
|
|
|
|
net_buf_unref(buf);
|
|
|
|
send_cmd_status(BT_HCI_OP_LE_P256_PUBLIC_KEY, 0);
|
|
|
|
buf = bt_buf_get_evt(BT_HCI_EVT_LE_META_EVENT);
|
|
if (!buf) {
|
|
BT_ERR("No available event buffers!");
|
|
return;
|
|
}
|
|
|
|
hdr = net_buf_add(buf, sizeof(*hdr));
|
|
hdr->evt = BT_HCI_EVT_LE_META_EVENT;
|
|
hdr->len = sizeof(*meta) + sizeof(*evt);
|
|
|
|
meta = net_buf_add(buf, sizeof(*meta));
|
|
meta->subevent = BT_HCI_EVT_LE_P256_PUBLIC_KEY_COMPLETE;
|
|
|
|
evt = net_buf_add(buf, sizeof(*evt));
|
|
evt->status = 0;
|
|
|
|
do {
|
|
uint32_t random[8];
|
|
|
|
if (bt_rand((uint8_t *)random, sizeof(random))) {
|
|
BT_ERR("Failed to get random bytes for ECC keys");
|
|
evt->status = 0x1f; /* unspecified error */
|
|
break;
|
|
}
|
|
|
|
if (ecc_make_key(&pkey, private_key, random) == TC_FAIL) {
|
|
BT_ERR("Failed to create ECC public/private pair");
|
|
evt->status = 0x1f; /* unspecified error */
|
|
break;
|
|
}
|
|
|
|
/* make sure generated key isn't debug key */
|
|
} while (memcmp(private_key, debug_private_key, 32) == 0);
|
|
|
|
if (!evt->status) {
|
|
memcpy(evt->key, pkey.x, 32);
|
|
memcpy(&evt->key[32], pkey.y, 32);
|
|
} else {
|
|
memset(evt->key, 0, sizeof(evt->key));
|
|
}
|
|
|
|
bt_recv(buf);
|
|
}
|
|
|
|
static void emulate_le_generate_dhkey(struct net_buf *buf)
|
|
{
|
|
struct bt_hci_evt_le_generate_dhkey_complete *evt;
|
|
struct bt_hci_cp_le_generate_dhkey *cmd;
|
|
struct bt_hci_evt_le_meta_event *meta;
|
|
struct bt_hci_evt_hdr *hdr;
|
|
EccPoint pk;
|
|
|
|
cmd = (void *)buf->data + sizeof(struct bt_hci_cmd_hdr);
|
|
|
|
/* TODO verify cmd parameters? */
|
|
send_cmd_status(BT_HCI_OP_LE_GENERATE_DHKEY, 0);
|
|
|
|
memcpy(pk.x, cmd->key, 32);
|
|
memcpy(pk.y, &cmd->key[32], 32);
|
|
|
|
net_buf_unref(buf);
|
|
|
|
buf = bt_buf_get_evt(BT_HCI_EVT_LE_META_EVENT);
|
|
if (!buf) {
|
|
BT_ERR("No available event buffers!");
|
|
return;
|
|
}
|
|
|
|
hdr = net_buf_add(buf, sizeof(*hdr));
|
|
hdr->evt = BT_HCI_EVT_LE_META_EVENT;
|
|
hdr->len = sizeof(*meta) + sizeof(*evt);
|
|
|
|
meta = net_buf_add(buf, sizeof(*meta));
|
|
meta->subevent = BT_HCI_EVT_LE_GENERATE_DHKEY_COMPLETE;
|
|
|
|
evt = net_buf_add(buf, sizeof(*evt));
|
|
evt->status = 0;
|
|
|
|
if (ecc_valid_public_key(&pk) < 0) {
|
|
evt->status = 0x1f; /* unspecified error */
|
|
memset(evt->dhkey, 0, sizeof(evt->dhkey));
|
|
bt_recv(buf);
|
|
return;
|
|
}
|
|
|
|
if (ecdh_shared_secret((uint32_t *)evt->dhkey, &pk, private_key)
|
|
== TC_FAIL) {
|
|
evt->status = 0x1f; /* unspecified error */
|
|
memset(evt->dhkey, 0, sizeof(evt->dhkey));
|
|
}
|
|
|
|
bt_recv(buf);
|
|
}
|
|
|
|
static void ecc_task(void)
|
|
{
|
|
nano_fifo_init(&ecc_queue);
|
|
|
|
while (true) {
|
|
struct net_buf *buf;
|
|
|
|
buf = nano_task_fifo_get(&ecc_queue, TICKS_UNLIMITED);
|
|
|
|
switch (bt_hci_get_cmd_opcode(buf)) {
|
|
case BT_HCI_OP_LE_P256_PUBLIC_KEY:
|
|
emulate_le_p256_public_key_cmd(buf);
|
|
break;
|
|
case BT_HCI_OP_LE_GENERATE_DHKEY:
|
|
emulate_le_generate_dhkey(buf);
|
|
break;
|
|
default:
|
|
BT_ERR("Unhandled command for ECC task (opcode %x)",
|
|
bt_hci_get_cmd_opcode(buf));
|
|
net_buf_unref(buf);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* TODO measure required stack size, 1024 is not enough */
|
|
DEFINE_TASK(ECC_TASKID, 10, ecc_task, 2048, EXE);
|
|
|
|
static void clear_ecc_events(struct net_buf *buf)
|
|
{
|
|
struct bt_hci_cp_le_set_event_mask *cmd;
|
|
|
|
cmd = (void *)buf->data + sizeof(struct bt_hci_cmd_hdr);
|
|
|
|
/*
|
|
* don't enable controller ECC events as those will be generated from
|
|
* emulation code
|
|
*/
|
|
cmd->events[0] &= ~0x80; /* LE Read Local P-256 PKey Compl */
|
|
cmd->events[1] &= ~0x01; /* LE Generate DHKey Compl Event */
|
|
}
|
|
|
|
static int ecc_send(struct net_buf *buf)
|
|
{
|
|
if (bt_buf_get_type(buf) == BT_BUF_CMD) {
|
|
switch (bt_hci_get_cmd_opcode(buf)) {
|
|
case BT_HCI_OP_LE_P256_PUBLIC_KEY:
|
|
case BT_HCI_OP_LE_GENERATE_DHKEY:
|
|
net_buf_put(&ecc_queue, buf);
|
|
return 0;
|
|
case BT_HCI_OP_LE_SET_EVENT_MASK:
|
|
clear_ecc_events(buf);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
return drv_send(buf);
|
|
}
|
|
|
|
void bt_hci_ecc_init(void)
|
|
{
|
|
/* set wrapper for driver send function */
|
|
drv_send = bt_dev.drv->send;
|
|
bt_dev.drv->send = ecc_send;
|
|
}
|