From 684ffd1e239fac4fab7928dbbda97cb1520f6865 Mon Sep 17 00:00:00 2001 From: Szymon Janc Date: Tue, 24 May 2016 17:15:03 +0200 Subject: [PATCH] Bluetooth: Offload ECC calculations to task If TinyCrypt ECC is used for LE SC calculations need to be done in task to avoid hogging CPU from non-preemptible fiber. To keep upper layers of stack independent of crypto used (TinyCrypt or controller) this patch adds HCI ECC emulation. If ECC emulation is enabled it is always used regardless of ECC support being present in controller. Change-Id: I7c5ca873a18c10237e1c0b2f09e6da2a75fb334e Origin: Original Signed-off-by: Szymon Janc --- net/bluetooth/Kconfig | 1 + net/bluetooth/Makefile | 1 + net/bluetooth/hci_core.c | 31 +++-- net/bluetooth/hci_core.h | 2 + net/bluetooth/hci_ecc.c | 248 +++++++++++++++++++++++++++++++++++++++ net/bluetooth/hci_ecc.h | 23 ++++ net/bluetooth/smp.c | 78 ------------ 7 files changed, 296 insertions(+), 88 deletions(-) create mode 100644 net/bluetooth/hci_ecc.c create mode 100644 net/bluetooth/hci_ecc.h diff --git a/net/bluetooth/Kconfig b/net/bluetooth/Kconfig index 2ba59113471..7e490d5505e 100644 --- a/net/bluetooth/Kconfig +++ b/net/bluetooth/Kconfig @@ -194,6 +194,7 @@ config BLUETOOTH_TINYCRYPT_ECC bool "Use TinyCrypt library for LE SC ECDH" default n select TINYCRYPT_ECC_DH + depends on MICROKERNEL help If this option is set TinyCrypt library is also used for LE Secure Connections elliptic curve Diffie-Hellman key agreement. If not set diff --git a/net/bluetooth/Makefile b/net/bluetooth/Makefile index 31f6430a44f..d0a3c8ef80d 100644 --- a/net/bluetooth/Makefile +++ b/net/bluetooth/Makefile @@ -19,5 +19,6 @@ ifeq ($(CONFIG_BLUETOOTH_CONN),y) obj-y += smp_null.o endif + obj-$(CONFIG_BLUETOOTH_TINYCRYPT_ECC) += hci_ecc.o obj-$(CONFIG_BLUETOOTH_BREDR) += keys.o l2cap_br.o endif diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index ffa60753d7e..17fd1808791 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -45,6 +45,7 @@ #include "keys.h" #include "monitor.h" #include "hci_core.h" +#include "hci_ecc.h" #if defined(CONFIG_BLUETOOTH_CONN) #include "conn_internal.h" @@ -1975,7 +1976,6 @@ done: bt_conn_unref(conn); } -#if !defined(CONFIG_TINYCRYPT_ECC_DH) static void le_pkey_complete(struct net_buf *buf) { struct bt_hci_evt_le_p256_public_key_complete *evt = (void *)buf->data; @@ -2002,7 +2002,6 @@ static void le_dhkey_complete(struct net_buf *buf) bt_smp_dhkey_ready(evt->dhkey); } -#endif /* !CONFIG_TINYCRYPT_ECC_DH */ #endif /* CONFIG_BLUETOOTH_SMP */ static void hci_reset_complete(struct net_buf *buf) @@ -2370,14 +2369,12 @@ static void hci_le_meta_event(struct net_buf *buf) case BT_HCI_EVT_LE_LTK_REQUEST: le_ltk_request(buf); break; -#if !defined(CONFIG_TINYCRYPT_ECC_DH) case BT_HCI_EVT_LE_P256_PUBLIC_KEY_COMPLETE: le_pkey_complete(buf); break; case BT_HCI_EVT_LE_GENERATE_DHKEY_COMPLETE: le_dhkey_complete(buf); break; -#endif /* !CONFIG_TINYCRYPT_ECC_DH */ #endif /* CONFIG_BLUETOOTH_SMP */ case BT_HCI_EVT_LE_ADVERTISING_REPORT: le_adv_report(buf); @@ -2622,6 +2619,15 @@ static void read_supported_commands_complete(struct net_buf *buf) memcpy(bt_dev.supported_commands, rp->commands, sizeof(bt_dev.supported_commands)); + +#if defined(CONFIG_BLUETOOTH_TINYCRYPT_ECC) + /* + * Report "LE Read Local P-256 Public Key" and "LE Generate DH Key" as + * supported if TinyCrypt ECC is used for emulation. + */ + bt_dev.supported_commands[34] |= 0x02; + bt_dev.supported_commands[34] |= 0x04; +#endif /* CONFIG_BLUETOOTH_TINYCRYPT_ECC */ } static int common_init(void) @@ -2750,18 +2756,16 @@ static int le_init(void) #if defined(CONFIG_BLUETOOTH_SMP) cp_mask->events[0] |= 0x10; /* LE Long Term Key Request Event */ -#if !defined(CONFIG_TINYCRYPT_ECC_DH) /* - * If controller based ECC is to be used and - * "LE Read Local P-256 Public Key" and "LE Generate DH Key" are + * If "LE Read Local P-256 Public Key" and "LE Generate DH Key" are * supported we need to enable events generated by those commands. */ + if ((bt_dev.supported_commands[34] & 0x02) && (bt_dev.supported_commands[34] & 0x04)) { cp_mask->events[0] |= 0x80; /* LE Read Local P-256 PKey Compl */ cp_mask->events[1] |= 0x01; /* LE Generate DHKey Compl Event */ } -#endif /* !CONFIG_TINYCRYPT_ECC_DH */ #endif /* CONFIG_BLUETOOTH_SMP */ err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_EVENT_MASK, buf, NULL); @@ -2769,7 +2773,7 @@ static int le_init(void) return err; } -#if defined(CONFIG_BLUETOOTH_SMP) && !defined(CONFIG_TINYCRYPT_ECC_DH) +#if defined(CONFIG_BLUETOOTH_SMP) /* * We check for both "LE Read Local P-256 Public Key" and * "LE Generate DH Key" support here since both commands are needed for @@ -2784,7 +2788,7 @@ static int le_init(void) return err; } } -#endif /* CONFIG_BLUETOOTH_SMP && !CONFIG_TINYCRYPT_ECC_DH*/ +#endif /* CONFIG_BLUETOOTH_SMP */ return prng_init(&prng); } @@ -3134,6 +3138,8 @@ static int bt_init(void) struct bt_driver *drv = bt_dev.drv; int err; + bt_hci_ecc_init(); + err = drv->open(); if (err) { BT_ERR("HCI driver open failed (%d)", err); @@ -3721,3 +3727,8 @@ int bt_storage_clear(bt_addr_le_t *addr) { return -ENOSYS; } + +uint16_t bt_hci_get_cmd_opcode(struct net_buf *buf) +{ + return cmd(buf)->opcode; +} diff --git a/net/bluetooth/hci_core.h b/net/bluetooth/hci_core.h index 677d512c90d..8891b54b103 100644 --- a/net/bluetooth/hci_core.h +++ b/net/bluetooth/hci_core.h @@ -184,3 +184,5 @@ int bt_le_scan_update(bool fast_scan); bool bt_addr_le_is_bonded(const bt_addr_le_t *addr); int bt_send(struct net_buf *buf); + +uint16_t bt_hci_get_cmd_opcode(struct net_buf *buf); diff --git a/net/bluetooth/hci_ecc.c b/net/bluetooth/hci_ecc.c new file mode 100644 index 00000000000..d449639cfe0 --- /dev/null +++ b/net/bluetooth/hci_ecc.c @@ -0,0 +1,248 @@ +/** + * @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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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(); + 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(); + 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(); + 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)); + nano_fifo_put(&bt_dev.rx_queue, 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: + nano_fifo_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; +} diff --git a/net/bluetooth/hci_ecc.h b/net/bluetooth/hci_ecc.h new file mode 100644 index 00000000000..ab62fb2a354 --- /dev/null +++ b/net/bluetooth/hci_ecc.h @@ -0,0 +1,23 @@ +/* hci_ecc.h - 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. + */ + +#if defined(CONFIG_BLUETOOTH_TINYCRYPT_ECC) +void bt_hci_ecc_init(void); +#else +#define bt_hci_ecc_init() +#endif /* CONFIG_BLUETOOTH_TINYCRYPT_ECC */ diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 4b124b26fc9..4c70de55b41 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -40,11 +40,6 @@ #include #include -#if defined(CONFIG_TINYCRYPT_ECC_DH) -#include -#include -#endif /* CONFIG_TINYCRYPT_ECC_DH */ - #include "hci_core.h" #include "keys.h" #include "conn_internal.h" @@ -213,10 +208,6 @@ static bool sc_supported; static bool sc_local_pkey_valid; static uint8_t sc_public_key[64]; -#if defined(CONFIG_TINYCRYPT_ECC_DH) -static uint32_t sc_private_key[8]; -#endif /* CONFIG_TINYCRYPT_ECC_DH */ - static uint8_t get_io_capa(void) { if (!bt_auth) { @@ -1872,7 +1863,6 @@ static uint8_t compute_and_check_and_send_slave_dhcheck(struct bt_smp *smp) } #endif /* CONFIG_BLUETOOTH_PERIPHERAL */ -#if !defined(CONFIG_TINYCRYPT_ECC_DH) void bt_smp_dhkey_ready(const uint8_t *dhkey) { struct bt_smp *smp = NULL; @@ -1925,7 +1915,6 @@ void bt_smp_dhkey_ready(const uint8_t *dhkey) #endif /* CONFIG_BLUETOOTH_PERIPHERAL */ } } -#endif /* !CONFIG_TINYCRYPT_ECC_DH */ static uint8_t sc_smp_check_confirm(struct bt_smp *smp) { @@ -2332,30 +2321,6 @@ static uint8_t smp_security_request(struct bt_smp *smp, struct net_buf *buf) } #endif /* CONFIG_BLUETOOTH_CENTRAL */ -#if defined(CONFIG_TINYCRYPT_ECC_DH) -static uint8_t generate_dhkey(struct bt_smp *smp) -{ - uint32_t dh[8]; - EccPoint pk; - - /* TODO on microkernel offload this to task? */ - - memcpy(pk.x, smp->pkey, 32); - memcpy(pk.y, &smp->pkey[32], 32); - - if (ecc_valid_public_key(&pk) < 0) { - return BT_SMP_ERR_DHKEY_CHECK_FAILED; - } - - if (ecdh_shared_secret(dh, &pk, sc_private_key) == TC_FAIL) { - return BT_SMP_ERR_DHKEY_CHECK_FAILED; - } - - memcpy(smp->dhkey, dh, 32); - - return 0; -} -#else static uint8_t generate_dhkey(struct bt_smp *smp) { struct bt_hci_cp_le_generate_dhkey *cp; @@ -2376,7 +2341,6 @@ static uint8_t generate_dhkey(struct bt_smp *smp) atomic_set_bit(&smp->flags, SMP_FLAG_DHKEY_PENDING); return 0; } -#endif /* CONFIG_TINYCRYPT_ECC_DH */ static uint8_t display_passkey(struct bt_smp *smp) { @@ -2635,7 +2599,6 @@ static void bt_smp_recv(struct bt_l2cap_chan *chan, struct net_buf *buf) } } -#if !defined(CONFIG_TINYCRYPT_ECC_DH) void bt_smp_pkey_ready(const uint8_t *pkey) { int i; @@ -2673,7 +2636,6 @@ void bt_smp_pkey_ready(const uint8_t *pkey) #endif /* CONFIG_BLUETOOTH_PERIPHERAL */ } } -#endif /* !CONFIG_TINYCRYPT_ECC_DH */ static void bt_smp_connected(struct bt_l2cap_chan *chan) { @@ -3583,45 +3545,6 @@ static int bt_smp_accept(struct bt_conn *conn, struct bt_l2cap_chan **chan) return -ENOMEM; } -#if defined(CONFIG_TINYCRYPT_ECC_DH) -#if defined(CONFIG_BLUETOOTH_USE_DEBUG_KEYS) -static bool le_sc_supported(void) -{ - memcpy(sc_private_key, sc_debug_private_key, 32); - memcpy(sc_public_key, sc_debug_public_key, 64); - - sc_local_pkey_valid = true; - - return true; -} -#else -static bool le_sc_supported(void) -{ - EccPoint pkey; - - do { - uint32_t random[8]; - - if (bt_rand((uint8_t *)random, sizeof(random))) { - return false; - } - - if (ecc_make_key(&pkey, sc_private_key, random) == TC_FAIL) { - BT_ERR("Failed to create ECC public/private pair"); - return false; - } - /* make sure generated key isn't debug key */ - } while (memcmp(sc_private_key, sc_debug_private_key, 32) == 0); - - memcpy(sc_public_key, pkey.x, 32); - memcpy(&sc_public_key[32], pkey.y, 32); - - sc_local_pkey_valid = true; - - return true; -} -#endif /* CONFIG_BLUETOOTH_USE_DEBUG_KEYS */ -#else static bool le_sc_supported(void) { /* @@ -3632,7 +3555,6 @@ static bool le_sc_supported(void) return (bt_dev.supported_commands[34] & 0x02) && (bt_dev.supported_commands[34] & 0x04); } -#endif int bt_smp_init(void) {