Bluetooth: Add support for persistent pairing keys storage
Integrate the bt_keys submodule with bt_settings. Add a new bt_keys_store() API to write keys to flash, and extend the existing bt_keys_clear() to remove the keys from flash. Along with this, add some helpers for genrating settings key values containing a bluetooth address, as well as for decoding them to get the binary bt_addr_le_t. Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
This commit is contained in:
parent
470349c25a
commit
f36ea83628
6 changed files with 228 additions and 20 deletions
|
@ -133,7 +133,7 @@ config BT_HOST_CRYPTO
|
|||
|
||||
config BT_SETTINGS
|
||||
bool "Store Bluetooth state and configuration persistently"
|
||||
depends on SETTINGS
|
||||
depends on SETTINGS && PRINTK
|
||||
select MPU_ALLOW_FLASH_WRITE if ARM_MPU
|
||||
help
|
||||
When selected, the Bluetooth stack will take care of storing
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
#include <atomic.h>
|
||||
#include <misc/util.h>
|
||||
|
||||
#include <settings/settings.h>
|
||||
|
||||
#include <bluetooth/bluetooth.h>
|
||||
#include <bluetooth/conn.h>
|
||||
#include <bluetooth/hci.h>
|
||||
|
@ -21,6 +23,7 @@
|
|||
#include "common/rpa.h"
|
||||
#include "hci_core.h"
|
||||
#include "smp.h"
|
||||
#include "settings.h"
|
||||
#include "keys.h"
|
||||
|
||||
static struct bt_keys key_pool[CONFIG_BT_MAX_PAIRED];
|
||||
|
@ -165,12 +168,22 @@ void bt_keys_add_type(struct bt_keys *keys, int type)
|
|||
|
||||
void bt_keys_clear(struct bt_keys *keys)
|
||||
{
|
||||
BT_DBG("keys for %s", bt_addr_le_str(&keys->addr));
|
||||
BT_DBG("%s (keys 0x%04x)", bt_addr_le_str(&keys->addr), keys->keys);
|
||||
|
||||
if (keys->keys & BT_KEYS_IRK) {
|
||||
bt_id_del(keys);
|
||||
}
|
||||
|
||||
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
|
||||
char key[BT_SETTINGS_KEY_MAX];
|
||||
|
||||
/* Delete stored keys from flash */
|
||||
bt_settings_encode_key(key, sizeof(key), "keys", &keys->addr,
|
||||
NULL);
|
||||
BT_DBG("Deleting key %s", key);
|
||||
settings_save_one(key, NULL);
|
||||
}
|
||||
|
||||
memset(keys, 0, sizeof(*keys));
|
||||
}
|
||||
|
||||
|
@ -178,3 +191,103 @@ void bt_keys_clear_all(void)
|
|||
{
|
||||
bt_keys_foreach(BT_KEYS_ALL, bt_keys_clear);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_BT_SETTINGS)
|
||||
int bt_keys_store(struct bt_keys *keys)
|
||||
{
|
||||
char val[BT_SETTINGS_SIZE(BT_KEYS_STORAGE_LEN)];
|
||||
char key[BT_SETTINGS_KEY_MAX];
|
||||
char *str;
|
||||
int err;
|
||||
|
||||
str = settings_str_from_bytes(keys->storage_start, BT_KEYS_STORAGE_LEN,
|
||||
val, sizeof(val));
|
||||
if (!str) {
|
||||
BT_ERR("Unable to encode bt_keys as value");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
bt_settings_encode_key(key, sizeof(key), "keys", &keys->addr, NULL);
|
||||
|
||||
err = settings_save_one(key, val);
|
||||
if (err) {
|
||||
BT_ERR("Failed to save keys (err %d)", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
BT_DBG("Stored keys for %s (%s)", bt_addr_le_str(&keys->addr), key);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int keys_set(int argc, char **argv, char *val)
|
||||
{
|
||||
struct bt_keys *keys;
|
||||
bt_addr_le_t addr;
|
||||
int len, err;
|
||||
|
||||
if (argc < 1) {
|
||||
BT_ERR("Insufficient number of arguments");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
BT_DBG("argv[0] %s val %s", argv[0], val ? val : "(null)");
|
||||
|
||||
err = bt_settings_decode_key(argv[0], &addr);
|
||||
if (err) {
|
||||
BT_ERR("Unable to decode address %s", argv[0]);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!val) {
|
||||
keys = bt_keys_find(BT_KEYS_ALL, &addr);
|
||||
if (keys) {
|
||||
memset(keys, 0, sizeof(*keys));
|
||||
BT_DBG("Cleared keys for %s", bt_addr_le_str(&addr));
|
||||
} else {
|
||||
BT_WARN("Unable to find deleted keys for %s",
|
||||
bt_addr_le_str(&addr));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
keys = bt_keys_get_addr(&addr);
|
||||
if (!keys) {
|
||||
BT_ERR("Failed to allocate keys for %s", bt_addr_le_str(&addr));
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
len = BT_KEYS_STORAGE_LEN;
|
||||
err = settings_bytes_from_str(val, keys->storage_start, &len);
|
||||
if (err) {
|
||||
BT_ERR("Failed to decode value (err %d)", err);
|
||||
bt_keys_clear(keys);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (len != BT_KEYS_STORAGE_LEN) {
|
||||
BT_ERR("Invalid key length %d != %d", len, BT_KEYS_STORAGE_LEN);
|
||||
bt_keys_clear(keys);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
BT_DBG("Successfully restored keys for %s", bt_addr_le_str(&addr));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int keys_commit(void)
|
||||
{
|
||||
BT_DBG("");
|
||||
|
||||
/* We do this in commit() rather than add() since add() may get
|
||||
* called multiple times for the same address, especially if
|
||||
* the keys were already removed.
|
||||
*/
|
||||
bt_keys_foreach(BT_KEYS_IRK, (bt_keys_func_t)bt_id_add);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
BT_SETTINGS_DEFINE(keys, keys_set, keys_commit, NULL);
|
||||
#endif /* CONFIG_BT_SETTINGS */
|
||||
|
|
|
@ -47,6 +47,7 @@ struct bt_csrk {
|
|||
|
||||
struct bt_keys {
|
||||
bt_addr_le_t addr;
|
||||
u8_t storage_start[0];
|
||||
u8_t enc_size;
|
||||
ATOMIC_DEFINE(flags, BT_KEYS_NUM_FLAGS);
|
||||
u16_t keys;
|
||||
|
@ -61,7 +62,11 @@ struct bt_keys {
|
|||
#endif /* CONFIG_BT_SMP_SC_ONLY */
|
||||
};
|
||||
|
||||
void bt_keys_foreach(int type, void (*func)(struct bt_keys *keys));
|
||||
#define BT_KEYS_STORAGE_LEN (sizeof(struct bt_keys) - \
|
||||
offsetof(struct bt_keys, storage_start))
|
||||
|
||||
typedef void (*bt_keys_func_t)(struct bt_keys *keys);
|
||||
void bt_keys_foreach(int type, bt_keys_func_t func);
|
||||
|
||||
struct bt_keys *bt_keys_get_addr(const bt_addr_le_t *addr);
|
||||
struct bt_keys *bt_keys_get_type(int type, const bt_addr_le_t *addr);
|
||||
|
@ -73,6 +78,15 @@ void bt_keys_add_type(struct bt_keys *keys, int type);
|
|||
void bt_keys_clear(struct bt_keys *keys);
|
||||
void bt_keys_clear_all(void);
|
||||
|
||||
#if defined(CONFIG_BT_SETTINGS)
|
||||
int bt_keys_store(struct bt_keys *keys);
|
||||
#else
|
||||
static inline int bt_keys_store(struct bt_keys *keys)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
enum {
|
||||
BT_LINK_KEY_AUTHENTICATED,
|
||||
BT_LINK_KEY_DEBUG,
|
||||
|
|
|
@ -22,11 +22,75 @@
|
|||
extern const struct bt_settings_handler _bt_settings_start[];
|
||||
extern const struct bt_settings_handler _bt_settings_end[];
|
||||
|
||||
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", path);
|
||||
}
|
||||
|
||||
int bt_settings_decode_key(char *key, bt_addr_le_t *addr)
|
||||
{
|
||||
bool high;
|
||||
int i;
|
||||
|
||||
if (strlen(key) != 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 (i = 5, high = true; i >= 0; key++) {
|
||||
u8_t nibble;
|
||||
|
||||
if (*key >= '0' && *key <= '9') {
|
||||
nibble = *key - '0';
|
||||
} else if (*key >= 'a' && *key <= 'f') {
|
||||
nibble = *key - 'a' + 10;
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (high) {
|
||||
addr->a.val[i] = nibble << 4;
|
||||
high = false;
|
||||
} else {
|
||||
addr->a.val[i] |= nibble;
|
||||
high = true;
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
BT_DBG("Decoded %s as %s", key, bt_addr_le_str(addr));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set(int argc, char **argv, char *val)
|
||||
{
|
||||
int len;
|
||||
|
||||
BT_DBG("argc %d argv[0] %s val %s", argc, argv[0], val);
|
||||
BT_DBG("argc %d argv[0] %s argv[1] %s val %s", argc, argv[0],
|
||||
argc > 1 ? argv[1] : "(null)", val ? val : "(null)");
|
||||
|
||||
if (argc > 1) {
|
||||
const struct bt_settings_handler *h;
|
||||
|
@ -64,7 +128,7 @@ static int set(int argc, char **argv, char *val)
|
|||
|
||||
static void generate_static_addr(void)
|
||||
{
|
||||
char buf[13];
|
||||
char buf[BT_SETTINGS_SIZE(sizeof(bt_dev.id_addr))];
|
||||
char *str;
|
||||
|
||||
BT_DBG("Generating new static random address");
|
||||
|
@ -92,7 +156,7 @@ static void generate_static_addr(void)
|
|||
#if defined(CONFIG_BT_PRIVACY)
|
||||
static void generate_irk(void)
|
||||
{
|
||||
char buf[25];
|
||||
char buf[BT_SETTINGS_SIZE(sizeof(bt_dev.irk))];
|
||||
char *str;
|
||||
|
||||
BT_DBG("Generating new IRK");
|
||||
|
|
|
@ -20,4 +20,15 @@ struct bt_settings_handler {
|
|||
.export = _export, \
|
||||
}
|
||||
|
||||
/* Max settings key length (with all components) */
|
||||
#define BT_SETTINGS_KEY_MAX 36
|
||||
|
||||
/* Base64-encoded string buffer size of in_size bytes */
|
||||
#define BT_SETTINGS_SIZE(in_size) ((((((in_size) - 1) / 3) * 4) + 4) + 1)
|
||||
|
||||
/* Helpers for keys containing a bdaddr */
|
||||
void bt_settings_encode_key(char *path, size_t path_size, const char *subsys,
|
||||
bt_addr_le_t *addr, const char *key);
|
||||
int bt_settings_decode_key(char *key, bt_addr_le_t *addr);
|
||||
|
||||
int bt_settings_init(void);
|
||||
|
|
|
@ -694,24 +694,27 @@ static void smp_br_reset(struct bt_smp_br *smp)
|
|||
|
||||
static void smp_pairing_br_complete(struct bt_smp_br *smp, u8_t status)
|
||||
{
|
||||
struct bt_conn *conn = smp->chan.chan.conn;
|
||||
struct bt_keys *keys;
|
||||
bt_addr_le_t addr;
|
||||
|
||||
BT_DBG("status 0x%x", status);
|
||||
|
||||
/* For dualmode devices LE address is same as BR/EDR address
|
||||
* and is of public type.
|
||||
*/
|
||||
bt_addr_copy(&addr.a, &conn->br.dst);
|
||||
addr.type = BT_ADDR_LE_PUBLIC;
|
||||
keys = bt_keys_find_addr(&addr);
|
||||
|
||||
if (status) {
|
||||
struct bt_conn *conn = smp->chan.chan.conn;
|
||||
struct bt_keys *keys;
|
||||
bt_addr_le_t addr;
|
||||
|
||||
/*
|
||||
* For dualmode devices LE address is same as BR/EDR address
|
||||
* and is of public type.
|
||||
*/
|
||||
bt_addr_copy(&addr.a, &conn->br.dst);
|
||||
addr.type = BT_ADDR_LE_PUBLIC;
|
||||
|
||||
keys = bt_keys_find_addr(&addr);
|
||||
if (keys) {
|
||||
bt_keys_clear(keys);
|
||||
}
|
||||
} else if (atomic_test_bit(smp->flags, SMP_FLAG_BOND)) {
|
||||
if (keys) {
|
||||
bt_keys_store(keys);
|
||||
}
|
||||
}
|
||||
|
||||
smp_br_reset(smp);
|
||||
|
@ -1469,8 +1472,8 @@ static void smp_pairing_complete(struct bt_smp *smp, u8_t status)
|
|||
{
|
||||
BT_DBG("status 0x%x", status);
|
||||
|
||||
#if defined(CONFIG_BT_BREDR)
|
||||
if (!status) {
|
||||
#if defined(CONFIG_BT_BREDR)
|
||||
/*
|
||||
* Don't derive if Debug Keys are used.
|
||||
* TODO should we allow this if BR/EDR is already connected?
|
||||
|
@ -1479,8 +1482,11 @@ static void smp_pairing_complete(struct bt_smp *smp, u8_t status)
|
|||
!atomic_test_bit(smp->flags, SMP_FLAG_SC_DEBUG_KEY)) {
|
||||
sc_derive_link_key(smp);
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_BT_BREDR */
|
||||
if (atomic_test_bit(smp->flags, SMP_FLAG_BOND)) {
|
||||
bt_keys_store(smp->chan.chan.conn->le.keys);
|
||||
}
|
||||
}
|
||||
|
||||
smp_reset(smp);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue