Bluetooth: br/edr: store link key

Save link key to settings_subsys, no need to re-pair after restart.
Overwrite old pairing records with aging counts.

Signed-off-by: ZhongYao Luo <LuoZhongYao@gmail.com>
This commit is contained in:
ZhongYao Luo 2020-01-03 09:28:45 +08:00 committed by Johan Hedberg
parent 2bfb128463
commit ddf5152bb8
4 changed files with 182 additions and 7 deletions

View file

@ -1088,7 +1088,16 @@ void bt_conn_security_changed(struct bt_conn *conn, enum bt_security_err err)
}
#if IS_ENABLED(CONFIG_BT_KEYS_OVERWRITE_OLDEST)
if (!err && conn->sec_level >= BT_SECURITY_L2) {
bt_keys_update_usage(conn->id, bt_conn_get_dst(conn));
if (conn->type == BT_CONN_TYPE_LE) {
bt_keys_update_usage(conn->id, bt_conn_get_dst(conn));
}
#if defined(CONFIG_BT_BREDR)
if (conn->type == BT_CONN_TYPE_BR) {
bt_keys_link_key_update_usage(&conn->br.dst);
}
#endif /* CONFIG_BT_BREDR */
}
#endif
}

View file

@ -3055,6 +3055,11 @@ static void link_key_notify(struct net_buf *buf)
break;
}
if (IS_ENABLED(CONFIG_BT_SETTINGS) &&
!atomic_test_bit(conn->flags, BT_CONN_BR_NOBOND)) {
bt_keys_link_key_store(conn->br.link_key);
}
bt_conn_unref(conn);
}

View file

@ -102,16 +102,25 @@ enum {
struct bt_keys_link_key {
bt_addr_t addr;
uint8_t flags;
uint8_t val[16];
uint8_t storage_start[0] __aligned(sizeof(void *));
uint8_t flags;
uint8_t val[16];
#if (defined(CONFIG_BT_KEYS_OVERWRITE_OLDEST))
uint32_t aging_counter;
#endif /* CONFIG_BT_KEYS_OVERWRITE_OLDEST */
};
#define BT_KEYS_LINK_KEY_STORAGE_LEN (sizeof(struct bt_keys_link_key) - \
offsetof(struct bt_keys_link_key, storage_start))
struct bt_keys_link_key *bt_keys_get_link_key(const bt_addr_t *addr);
struct bt_keys_link_key *bt_keys_find_link_key(const bt_addr_t *addr);
void bt_keys_link_key_clear(struct bt_keys_link_key *link_key);
void bt_keys_link_key_clear_addr(const bt_addr_t *addr);
void bt_keys_link_key_store(struct bt_keys_link_key *link_key);
/* This function is used to signal that the key has been used for paring */
/* It updates the aging counter and saves it to flash if configuration option */
/* BT_KEYS_SAVE_AGING_COUNTER_ON_PAIRING is enabled */
void bt_keys_update_usage(uint8_t id, const bt_addr_le_t *addr);
void bt_keys_link_key_update_usage(const bt_addr_t *addr);

View file

@ -14,16 +14,23 @@
#include <bluetooth/bluetooth.h>
#include <bluetooth/conn.h>
#include <bluetooth/hci.h>
#include <settings/settings.h>
#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_KEYS)
#define LOG_MODULE_NAME bt_keys_br
#include "common/log.h"
#include "hci_core.h"
#include "settings.h"
#include "keys.h"
static struct bt_keys_link_key key_pool[CONFIG_BT_MAX_PAIRED];
#if IS_ENABLED(CONFIG_BT_KEYS_OVERWRITE_OLDEST)
static uint32_t aging_counter_val;
static struct bt_keys_link_key *last_keys_updated;
#endif /* CONFIG_BT_KEYS_OVERWRITE_OLDEST */
struct bt_keys_link_key *bt_keys_find_link_key(const bt_addr_t *addr)
{
struct bt_keys_link_key *key;
@ -52,31 +59,67 @@ struct bt_keys_link_key *bt_keys_get_link_key(const bt_addr_t *addr)
}
key = bt_keys_find_link_key(BT_ADDR_ANY);
#if IS_ENABLED(CONFIG_BT_KEYS_OVERWRITE_OLDEST)
if (!key) {
int i;
key = &key_pool[0];
for (i = 1; i < ARRAY_SIZE(key_pool); i++) {
struct bt_keys_link_key *current = &key_pool[i];
if (current->aging_counter < key->aging_counter) {
key = current;
}
}
if (key) {
bt_keys_link_key_clear(key);
}
}
#endif
if (key) {
bt_addr_copy(&key->addr, addr);
#if IS_ENABLED(CONFIG_BT_KEYS_OVERWRITE_OLDEST)
key->aging_counter = ++aging_counter_val;
last_keys_updated = key;
#endif
BT_DBG("created %p for %s", key, bt_addr_str(addr));
return key;
}
BT_DBG("unable to create link key for %s", bt_addr_str(addr));
BT_DBG("unable to create keys for %s", bt_addr_str(addr));
return NULL;
}
void bt_keys_link_key_clear(struct bt_keys_link_key *link_key)
{
BT_DBG("%s", bt_addr_str(&link_key->addr));
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
char key[BT_SETTINGS_KEY_MAX];
bt_addr_le_t le_addr;
le_addr.type = BT_ADDR_LE_PUBLIC;
bt_addr_copy(&le_addr.a, &link_key->addr);
bt_settings_encode_key(key, sizeof(key), "link_key",
&le_addr, NULL);
settings_delete(key);
}
BT_DBG("%s", bt_addr_str(&link_key->addr));
(void)memset(link_key, 0, sizeof(*link_key));
}
void bt_keys_link_key_clear_addr(const bt_addr_t *addr)
{
int i;
struct bt_keys_link_key *key;
if (!addr) {
(void)memset(key_pool, 0, sizeof(key_pool));
for (i = 0; i < ARRAY_SIZE(key_pool); i++) {
key = &key_pool[i];
bt_keys_link_key_clear(key);
}
return;
}
@ -85,3 +128,112 @@ void bt_keys_link_key_clear_addr(const bt_addr_t *addr)
bt_keys_link_key_clear(key);
}
}
void bt_keys_link_key_store(struct bt_keys_link_key *link_key)
{
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
int err;
char key[BT_SETTINGS_KEY_MAX];
bt_addr_le_t le_addr;
le_addr.type = BT_ADDR_LE_PUBLIC;
bt_addr_copy(&le_addr.a, &link_key->addr);
bt_settings_encode_key(key, sizeof(key), "link_key",
&le_addr, NULL);
err = settings_save_one(key, link_key->storage_start,
BT_KEYS_LINK_KEY_STORAGE_LEN);
if (err) {
BT_ERR("Failed to svae link key (err %d)", err);
}
}
}
#if defined(CONFIG_BT_SETTINGS)
static int link_key_set(const char *name, size_t len_rd,
settings_read_cb read_cb, void *cb_arg)
{
int err;
ssize_t len;
bt_addr_le_t le_addr;
struct bt_keys_link_key *link_key;
char val[BT_KEYS_LINK_KEY_STORAGE_LEN];
if (!name) {
BT_ERR("Insufficient number of arguments");
return -EINVAL;
}
len = read_cb(cb_arg, val, sizeof(val));
if (len < 0) {
BT_ERR("Failed to read value (err %zu)", len);
return -EINVAL;
}
BT_DBG("name %s val %s", log_strdup(name),
len ? bt_hex(val, sizeof(val)) : "(null)");
err = bt_settings_decode_key(name, &le_addr);
if (err) {
BT_ERR("Unable to decode address %s", name);
return -EINVAL;
}
link_key = bt_keys_get_link_key(&le_addr.a);
if (len != BT_KEYS_LINK_KEY_STORAGE_LEN) {
if (link_key) {
bt_keys_link_key_clear(link_key);
BT_DBG("Clear keys for %s", bt_addr_le_str(&le_addr));
} else {
BT_WARN("Unable to find deleted keys for %s",
bt_addr_le_str(&le_addr));
}
return 0;
}
memcpy(link_key->storage_start, val, len);
BT_DBG("Successfully restored link key for %s",
bt_addr_le_str(&le_addr));
#if IS_ENABLED(CONFIG_BT_KEYS_OVERWRITE_OLDEST)
if (aging_counter_val < link_key->aging_counter) {
aging_counter_val = link_key->aging_counter;
}
#endif /* CONFIG_BT_KEYS_OVERWRITE_OLDEST */
return 0;
}
static int link_key_commit(void)
{
return 0;
}
SETTINGS_STATIC_HANDLER_DEFINE(bt_link_key, "bt/link_key", NULL, link_key_set,
link_key_commit, NULL);
void bt_keys_link_key_update_usage(const bt_addr_t *addr)
{
struct bt_keys_link_key *link_key = bt_keys_find_link_key(addr);
if (!link_key) {
return;
}
if (last_keys_updated == link_key) {
return;
}
link_key->aging_counter = ++aging_counter_val;
last_keys_updated = link_key;
BT_DBG("Aging counter for %s is set to %u", bt_addr_str(addr),
link_key->aging_counter);
if (IS_ENABLED(CONFIG_BT_KEYS_SAVE_AGING_COUNTER_ON_PAIRING)) {
bt_keys_link_key_store(link_key);
}
}
#endif