Fix multiple issues related to the way the host handles Identity Information related to privacy 1. If the controller provided a public address the IRK for this identity would be randomly generated but not stored persistenly. 2. Fix the handling of the above issue which was fixed for the random address but would initiate settings save ID on every boot. 3. Fix the host not using the Vendor Specific HCI commands related to retrieving the Identity Root (IR) from the controller and using the key diversified function d1 to generate an IRK as specified in the BT Core spec. Make sure that a Host generated ID is only saved when it is first generated. Signed-off-by: Joakim Andersson <joakim.andersson@nordicsemi.no>
286 lines
6.1 KiB
C
286 lines
6.1 KiB
C
/*
|
|
* Copyright (c) 2018 Intel Corporation
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <errno.h>
|
|
|
|
#include <zephyr.h>
|
|
#include <settings/settings.h>
|
|
|
|
#include <bluetooth/bluetooth.h>
|
|
#include <bluetooth/conn.h>
|
|
|
|
#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_SETTINGS)
|
|
#define LOG_MODULE_NAME bt_settings
|
|
#include "common/log.h"
|
|
|
|
#include "hci_core.h"
|
|
#include "settings.h"
|
|
|
|
#if defined(CONFIG_BT_SETTINGS_USE_PRINTK)
|
|
void bt_settings_encode_key(char *path, size_t path_size, const char *subsys,
|
|
bt_addr_le_t *addr, const char *key)
|
|
{
|
|
if (key) {
|
|
snprintk(path, path_size,
|
|
"bt/%s/%02x%02x%02x%02x%02x%02x%u/%s", subsys,
|
|
addr->a.val[5], addr->a.val[4], addr->a.val[3],
|
|
addr->a.val[2], addr->a.val[1], addr->a.val[0],
|
|
addr->type, key);
|
|
} else {
|
|
snprintk(path, path_size,
|
|
"bt/%s/%02x%02x%02x%02x%02x%02x%u", subsys,
|
|
addr->a.val[5], addr->a.val[4], addr->a.val[3],
|
|
addr->a.val[2], addr->a.val[1], addr->a.val[0],
|
|
addr->type);
|
|
}
|
|
|
|
BT_DBG("Encoded path %s", log_strdup(path));
|
|
}
|
|
#else
|
|
void bt_settings_encode_key(char *path, size_t path_size, const char *subsys,
|
|
bt_addr_le_t *addr, const char *key)
|
|
{
|
|
size_t len = 3;
|
|
|
|
/* Skip if path_size is less than 3; strlen("bt/") */
|
|
if (len < path_size) {
|
|
/* Key format:
|
|
* "bt/<subsys>/<addr><type>/<key>", "/<key>" is optional
|
|
*/
|
|
strcpy(path, "bt/");
|
|
strncpy(&path[len], subsys, path_size - len);
|
|
len = strlen(path);
|
|
if (len < path_size) {
|
|
path[len] = '/';
|
|
len++;
|
|
}
|
|
|
|
for (s8_t i = 5; i >= 0 && len < path_size; i--) {
|
|
len += bin2hex(&addr->a.val[i], 1, &path[len],
|
|
path_size - len);
|
|
}
|
|
|
|
if (len < path_size) {
|
|
/* Type can be either BT_ADDR_LE_PUBLIC or
|
|
* BT_ADDR_LE_RANDOM (value 0 or 1)
|
|
*/
|
|
path[len] = '0' + addr->type;
|
|
len++;
|
|
}
|
|
|
|
if (key && len < path_size) {
|
|
path[len] = '/';
|
|
len++;
|
|
strncpy(&path[len], key, path_size - len);
|
|
len += strlen(&path[len]);
|
|
}
|
|
|
|
if (len >= path_size) {
|
|
/* Truncate string */
|
|
path[path_size - 1] = '\0';
|
|
}
|
|
} else if (path_size > 0) {
|
|
*path = '\0';
|
|
}
|
|
|
|
BT_DBG("Encoded path %s", log_strdup(path));
|
|
}
|
|
#endif
|
|
|
|
int bt_settings_decode_key(const char *key, bt_addr_le_t *addr)
|
|
{
|
|
if (settings_name_next(key, NULL) != 13) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (key[12] == '0') {
|
|
addr->type = BT_ADDR_LE_PUBLIC;
|
|
} else if (key[12] == '1') {
|
|
addr->type = BT_ADDR_LE_RANDOM;
|
|
} else {
|
|
return -EINVAL;
|
|
}
|
|
|
|
for (u8_t i = 0; i < 6; i++) {
|
|
hex2bin(&key[i * 2], 2, &addr->a.val[5 - i], 1);
|
|
}
|
|
|
|
BT_DBG("Decoded %s as %s", log_strdup(key), bt_addr_le_str(addr));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int set(const char *name, size_t len_rd, settings_read_cb read_cb,
|
|
void *cb_arg)
|
|
{
|
|
ssize_t len;
|
|
const char *next;
|
|
|
|
if (!name) {
|
|
BT_ERR("Insufficient number of arguments");
|
|
return -ENOENT;
|
|
}
|
|
|
|
len = settings_name_next(name, &next);
|
|
|
|
if (!strncmp(name, "id", len)) {
|
|
/* Any previously provided identities supersede flash */
|
|
if (atomic_test_bit(bt_dev.flags, BT_DEV_PRESET_ID)) {
|
|
BT_WARN("Ignoring identities stored in flash");
|
|
return 0;
|
|
}
|
|
|
|
len = read_cb(cb_arg, &bt_dev.id_addr, sizeof(bt_dev.id_addr));
|
|
if (len < sizeof(bt_dev.id_addr[0])) {
|
|
if (len < 0) {
|
|
BT_ERR("Failed to read ID address from storage"
|
|
" (err %zu)", len);
|
|
} else {
|
|
BT_ERR("Invalid length ID address in storage");
|
|
BT_HEXDUMP_DBG(&bt_dev.id_addr, len,
|
|
"data read");
|
|
}
|
|
(void)memset(bt_dev.id_addr, 0,
|
|
sizeof(bt_dev.id_addr));
|
|
bt_dev.id_count = 0U;
|
|
} else {
|
|
int i;
|
|
|
|
bt_dev.id_count = len / sizeof(bt_dev.id_addr[0]);
|
|
for (i = 0; i < bt_dev.id_count; i++) {
|
|
BT_DBG("ID[%d] %s", i,
|
|
bt_addr_le_str(&bt_dev.id_addr[i]));
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if defined(CONFIG_BT_DEVICE_NAME_DYNAMIC)
|
|
if (!strncmp(name, "name", len)) {
|
|
len = read_cb(cb_arg, &bt_dev.name, sizeof(bt_dev.name) - 1);
|
|
if (len < 0) {
|
|
BT_ERR("Failed to read device name from storage"
|
|
" (err %zu)", len);
|
|
} else {
|
|
bt_dev.name[len] = '\0';
|
|
|
|
BT_DBG("Name set to %s", log_strdup(bt_dev.name));
|
|
}
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
#if defined(CONFIG_BT_PRIVACY)
|
|
if (!strncmp(name, "irk", len)) {
|
|
len = read_cb(cb_arg, bt_dev.irk, sizeof(bt_dev.irk));
|
|
if (len < sizeof(bt_dev.irk[0])) {
|
|
if (len < 0) {
|
|
BT_ERR("Failed to read IRK from storage"
|
|
" (err %zu)", len);
|
|
} else {
|
|
BT_ERR("Invalid length IRK in storage");
|
|
(void)memset(bt_dev.irk, 0, sizeof(bt_dev.irk));
|
|
}
|
|
} else {
|
|
int i, count;
|
|
|
|
count = len / sizeof(bt_dev.irk[0]);
|
|
for (i = 0; i < count; i++) {
|
|
BT_DBG("IRK[%d] %s", i,
|
|
bt_hex(bt_dev.irk[i], 16));
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif /* CONFIG_BT_PRIVACY */
|
|
|
|
return -ENOENT;
|
|
}
|
|
|
|
#define ID_DATA_LEN(array) (bt_dev.id_count * sizeof(array[0]))
|
|
|
|
static void save_id(struct k_work *work)
|
|
{
|
|
int err;
|
|
BT_INFO("Saving ID");
|
|
err = settings_save_one("bt/id", &bt_dev.id_addr,
|
|
ID_DATA_LEN(bt_dev.id_addr));
|
|
if (err) {
|
|
BT_ERR("Failed to save ID (err %d)", err);
|
|
}
|
|
|
|
#if defined(CONFIG_BT_PRIVACY)
|
|
err = settings_save_one("bt/irk", bt_dev.irk, ID_DATA_LEN(bt_dev.irk));
|
|
if (err) {
|
|
BT_ERR("Failed to save IRK (err %d)", err);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
K_WORK_DEFINE(save_id_work, save_id);
|
|
|
|
void bt_settings_save_id(void)
|
|
{
|
|
k_work_submit(&save_id_work);
|
|
}
|
|
|
|
static int commit(void)
|
|
{
|
|
BT_DBG("");
|
|
|
|
#if defined(CONFIG_BT_DEVICE_NAME_DYNAMIC)
|
|
if (bt_dev.name[0] == '\0') {
|
|
bt_set_name(CONFIG_BT_DEVICE_NAME);
|
|
}
|
|
#endif
|
|
if (!bt_dev.id_count) {
|
|
bt_setup_public_id_addr();
|
|
}
|
|
|
|
if (!bt_dev.id_count) {
|
|
int err;
|
|
|
|
err = bt_setup_random_id_addr();
|
|
if (err) {
|
|
BT_ERR("Unable to setup an identity address");
|
|
return err;
|
|
}
|
|
}
|
|
|
|
if (!atomic_test_bit(bt_dev.flags, BT_DEV_READY)) {
|
|
bt_finalize_init();
|
|
}
|
|
|
|
/* If any part of the Identity Information of the device has been
|
|
* generated this Identity needs to be saved persistently.
|
|
*/
|
|
if (atomic_test_and_clear_bit(bt_dev.flags, BT_DEV_STORE_ID)) {
|
|
BT_DBG("Storing Identity Information");
|
|
bt_settings_save_id();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
SETTINGS_STATIC_HANDLER_DEFINE(bt, "bt", NULL, set, commit, NULL);
|
|
|
|
int bt_settings_init(void)
|
|
{
|
|
int err;
|
|
|
|
BT_DBG("");
|
|
|
|
err = settings_subsys_init();
|
|
if (err) {
|
|
BT_ERR("settings_subsys_init failed (err %d)", err);
|
|
return err;
|
|
}
|
|
|
|
return 0;
|
|
}
|