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:
Johan Hedberg 2018-04-27 20:01:51 +03:00 committed by Anas Nashif
commit f36ea83628
6 changed files with 228 additions and 20 deletions

View file

@ -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

View file

@ -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 */

View file

@ -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,

View file

@ -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");

View file

@ -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);

View file

@ -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);
}