| /* |
| * Copyright (c) 2017 Nordic Semiconductor ASA |
| * Copyright (c) 2015 Intel Corporation |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <stdint.h> |
| |
| #include <zephyr/sys/check.h> |
| #include <zephyr/bluetooth/hci.h> |
| |
| #include "ecc.h" |
| #include "hci_core.h" |
| |
| #define LOG_LEVEL CONFIG_BT_HCI_CORE_LOG_LEVEL |
| #include <zephyr/logging/log.h> |
| LOG_MODULE_REGISTER(bt_ecc); |
| |
| static uint8_t pub_key[BT_PUB_KEY_LEN]; |
| static sys_slist_t pub_key_cb_slist; |
| static bt_dh_key_cb_t dh_key_cb; |
| |
| static const uint8_t debug_public_key[BT_PUB_KEY_LEN] = { |
| /* X */ |
| 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, |
| /* Y */ |
| 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 |
| }; |
| |
| bool bt_pub_key_is_debug(uint8_t *cmp_pub_key) |
| { |
| return memcmp(cmp_pub_key, debug_public_key, BT_PUB_KEY_LEN) == 0; |
| } |
| |
| int bt_pub_key_gen(struct bt_pub_key_cb *new_cb) |
| { |
| struct bt_pub_key_cb *cb; |
| int err; |
| |
| /* |
| * We check for both "LE Read Local P-256 Public Key" and |
| * "LE Generate DH Key" support here since both commands are needed for |
| * ECC support. If "LE Generate DH Key" is not supported then there |
| * is no point in reading local public key. |
| */ |
| if (!BT_CMD_TEST(bt_dev.supported_commands, 34, 1) || |
| !BT_CMD_TEST(bt_dev.supported_commands, 34, 2)) { |
| LOG_WRN("ECC HCI commands not available"); |
| return -ENOTSUP; |
| } |
| |
| if (IS_ENABLED(CONFIG_BT_USE_DEBUG_KEYS)) { |
| if (!BT_CMD_TEST(bt_dev.supported_commands, 41, 2)) { |
| LOG_WRN("ECC Debug keys HCI command not available"); |
| } else { |
| atomic_set_bit(bt_dev.flags, BT_DEV_HAS_PUB_KEY); |
| __ASSERT_NO_MSG(new_cb->func != NULL); |
| new_cb->func(debug_public_key); |
| return 0; |
| } |
| } |
| |
| if (!new_cb) { |
| return -EINVAL; |
| } |
| |
| SYS_SLIST_FOR_EACH_CONTAINER(&pub_key_cb_slist, cb, node) { |
| if (cb == new_cb) { |
| LOG_WRN("Callback already registered"); |
| return -EALREADY; |
| } |
| } |
| |
| sys_slist_prepend(&pub_key_cb_slist, &new_cb->node); |
| |
| if (atomic_test_and_set_bit(bt_dev.flags, BT_DEV_PUB_KEY_BUSY)) { |
| return 0; |
| } |
| |
| atomic_clear_bit(bt_dev.flags, BT_DEV_HAS_PUB_KEY); |
| |
| err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_P256_PUBLIC_KEY, NULL, NULL); |
| if (err) { |
| |
| LOG_ERR("Sending LE P256 Public Key command failed"); |
| atomic_clear_bit(bt_dev.flags, BT_DEV_PUB_KEY_BUSY); |
| |
| SYS_SLIST_FOR_EACH_CONTAINER(&pub_key_cb_slist, cb, node) { |
| if (cb->func) { |
| cb->func(NULL); |
| } |
| } |
| |
| sys_slist_init(&pub_key_cb_slist); |
| return err; |
| } |
| |
| return 0; |
| } |
| |
| void bt_pub_key_hci_disrupted(void) |
| { |
| struct bt_pub_key_cb *cb; |
| |
| atomic_clear_bit(bt_dev.flags, BT_DEV_PUB_KEY_BUSY); |
| |
| SYS_SLIST_FOR_EACH_CONTAINER(&pub_key_cb_slist, cb, node) { |
| if (cb->func) { |
| cb->func(NULL); |
| } |
| } |
| |
| sys_slist_init(&pub_key_cb_slist); |
| } |
| |
| const uint8_t *bt_pub_key_get(void) |
| { |
| if (IS_ENABLED(CONFIG_BT_USE_DEBUG_KEYS) && |
| BT_CMD_TEST(bt_dev.supported_commands, 41, 2)) { |
| return debug_public_key; |
| } |
| |
| if (atomic_test_bit(bt_dev.flags, BT_DEV_HAS_PUB_KEY)) { |
| return pub_key; |
| } |
| |
| return NULL; |
| } |
| |
| static int hci_generate_dhkey_v1(const uint8_t *remote_pk) |
| { |
| struct bt_hci_cp_le_generate_dhkey *cp; |
| struct net_buf *buf; |
| |
| buf = bt_hci_cmd_create(BT_HCI_OP_LE_GENERATE_DHKEY, sizeof(*cp)); |
| if (!buf) { |
| return -ENOBUFS; |
| } |
| |
| cp = net_buf_add(buf, sizeof(*cp)); |
| memcpy(cp->key, remote_pk, sizeof(cp->key)); |
| |
| return bt_hci_cmd_send_sync(BT_HCI_OP_LE_GENERATE_DHKEY, buf, NULL); |
| } |
| |
| static int hci_generate_dhkey_v2(const uint8_t *remote_pk, uint8_t key_type) |
| { |
| struct bt_hci_cp_le_generate_dhkey_v2 *cp; |
| struct net_buf *buf; |
| |
| buf = bt_hci_cmd_create(BT_HCI_OP_LE_GENERATE_DHKEY_V2, sizeof(*cp)); |
| if (!buf) { |
| return -ENOBUFS; |
| } |
| |
| cp = net_buf_add(buf, sizeof(*cp)); |
| memcpy(cp->key, remote_pk, sizeof(cp->key)); |
| cp->key_type = key_type; |
| |
| return bt_hci_cmd_send_sync(BT_HCI_OP_LE_GENERATE_DHKEY_V2, buf, NULL); |
| } |
| |
| int bt_dh_key_gen(const uint8_t remote_pk[BT_PUB_KEY_LEN], bt_dh_key_cb_t cb) |
| { |
| int err; |
| |
| if (dh_key_cb == cb) { |
| return -EALREADY; |
| } |
| |
| if (dh_key_cb || atomic_test_bit(bt_dev.flags, BT_DEV_PUB_KEY_BUSY)) { |
| return -EBUSY; |
| } |
| |
| if (!atomic_test_bit(bt_dev.flags, BT_DEV_HAS_PUB_KEY)) { |
| return -EADDRNOTAVAIL; |
| } |
| |
| dh_key_cb = cb; |
| |
| if (IS_ENABLED(CONFIG_BT_USE_DEBUG_KEYS) && |
| BT_CMD_TEST(bt_dev.supported_commands, 41, 2)) { |
| err = hci_generate_dhkey_v2(remote_pk, |
| BT_HCI_LE_KEY_TYPE_DEBUG); |
| } else { |
| err = hci_generate_dhkey_v1(remote_pk); |
| } |
| |
| if (err) { |
| dh_key_cb = NULL; |
| LOG_WRN("Failed to generate DHKey (err %d)", err); |
| return err; |
| } |
| |
| return 0; |
| } |
| |
| void bt_hci_evt_le_pkey_complete(struct net_buf *buf) |
| { |
| struct bt_hci_evt_le_p256_public_key_complete *evt = (void *)buf->data; |
| struct bt_pub_key_cb *cb; |
| |
| LOG_DBG("status: 0x%02x %s", evt->status, bt_hci_err_to_str(evt->status)); |
| |
| atomic_clear_bit(bt_dev.flags, BT_DEV_PUB_KEY_BUSY); |
| |
| if (!evt->status) { |
| memcpy(pub_key, evt->key, BT_PUB_KEY_LEN); |
| atomic_set_bit(bt_dev.flags, BT_DEV_HAS_PUB_KEY); |
| } |
| |
| SYS_SLIST_FOR_EACH_CONTAINER(&pub_key_cb_slist, cb, node) { |
| if (cb->func) { |
| cb->func(evt->status ? NULL : pub_key); |
| } |
| } |
| |
| sys_slist_init(&pub_key_cb_slist); |
| } |
| |
| void bt_hci_evt_le_dhkey_complete(struct net_buf *buf) |
| { |
| struct bt_hci_evt_le_generate_dhkey_complete *evt = (void *)buf->data; |
| |
| LOG_DBG("status: 0x%02x %s", evt->status, bt_hci_err_to_str(evt->status)); |
| |
| if (dh_key_cb) { |
| bt_dh_key_cb_t cb = dh_key_cb; |
| |
| dh_key_cb = NULL; |
| cb(evt->status ? NULL : evt->dhkey); |
| } |
| } |
| |
| #ifdef ZTEST_UNITTEST |
| uint8_t const *bt_ecc_get_public_key(void) |
| { |
| return pub_key; |
| } |
| |
| uint8_t const *bt_ecc_get_internal_debug_public_key(void) |
| { |
| return debug_public_key; |
| } |
| |
| sys_slist_t *bt_ecc_get_pub_key_cb_slist(void) |
| { |
| return &pub_key_cb_slist; |
| } |
| |
| bt_dh_key_cb_t *bt_ecc_get_dh_key_cb(void) |
| { |
| return &dh_key_cb; |
| } |
| #endif /* ZTEST_UNITTEST */ |