Bluetooth: Keys: add key overwrite feature for key storage.

Key overwrite feature allows to overwrite old pairing key
records when key storage is full and a new pairing request occurs,
or new keys are distributed. If enabled when key storage is full and
a keys storage slot is requested, the oldest keys added will be
removed. So new devices can be paired with no limitations and no need
to determine, which devices should be unpaired to free key storage
space explicitly in application. To enable the feature set
CONFIG_BT_KEYS_OVERWRITE_OLDEST=y.

Oldest keys are determined by minimum value of up-counting aging
counter. If you set CONFIG_BT_KEYS_SAVE_AGING_COUNTER_ON_PAIRING=y
aging counter values will be updated each time the secure connection
is established. This might increase flash wear out if at least two
secure connections are established and shut down periodically. When
the option disabled aging counter is still updated on each new secure
connection, but not stored to flash.

Signed-off-by: Sergiy Nikolayenko <sergiy_nikolayenko@jabil.com>
This commit is contained in:
Sergiy Nikolayenko 2019-09-26 17:27:50 +03:00 committed by Johan Hedberg
commit 25164654dd
5 changed files with 111 additions and 6 deletions

View file

@ -16,6 +16,7 @@ CONFIG_BT_DEVICE_APPEARANCE=833
CONFIG_BT_DEVICE_NAME_DYNAMIC=y
CONFIG_BT_DEVICE_NAME_MAX=65
CONFIG_BT_KEYS_OVERWRITE_OLDEST=y
CONFIG_BT_SETTINGS=y
CONFIG_FLASH=y
CONFIG_FLASH_PAGE_LAYOUT=y

View file

@ -351,6 +351,21 @@ config BT_OOB_DATA_FIXED
value. This option should only be enabled for debugging and should
never be used in production.
config BT_KEYS_OVERWRITE_OLDEST
bool "Overwrite oldest keys with new ones if key storage is full"
help
With this option enabled, if a pairing attempt occurs and the key storage
is full, then the oldest keys in storage will be removed to free space
for the new pairing keys.
config BT_KEYS_SAVE_AGING_COUNTER_ON_PAIRING
bool "Store aging counter every time a successful paring occurs"
depends on BT_SETTINGS && BT_KEYS_OVERWRITE_OLDEST
help
With this option enabled, aging counter will be stored in settings every
time a successful pairing occurs. This increases flash wear out but offers
a more correct finding of the oldest unused paiting info.
endif # BT_SMP
source "subsys/bluetooth/host/Kconfig.l2cap"

View file

@ -1007,6 +1007,11 @@ void bt_conn_security_changed(struct bt_conn *conn, enum bt_security_err err)
cb->security_changed(conn, conn->sec_level, 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));
}
#endif
}
static int start_security(struct bt_conn *conn)

View file

@ -31,6 +31,13 @@
static struct bt_keys key_pool[CONFIG_BT_MAX_PAIRED];
#define BT_KEYS_STORAGE_LEN_COMPAT (BT_KEYS_STORAGE_LEN - sizeof(uint32_t))
#if IS_ENABLED(CONFIG_BT_KEYS_OVERWRITE_OLDEST)
static u32_t aging_counter_val;
static struct bt_keys *last_keys_updated;
#endif /* CONFIG_BT_KEYS_OVERWRITE_OLDEST */
struct bt_keys *bt_keys_get_addr(u8_t id, const bt_addr_le_t *addr)
{
struct bt_keys *keys;
@ -47,18 +54,43 @@ struct bt_keys *bt_keys_get_addr(u8_t id, const bt_addr_le_t *addr)
}
if (first_free_slot == ARRAY_SIZE(key_pool) &&
!bt_addr_le_cmp(&keys->addr, BT_ADDR_LE_ANY)) {
(!bt_addr_le_cmp(&keys->addr, BT_ADDR_LE_ANY) ||
!keys->enc_size)) {
first_free_slot = i;
}
}
#if IS_ENABLED(CONFIG_BT_KEYS_OVERWRITE_OLDEST)
if (first_free_slot == ARRAY_SIZE(key_pool)) {
struct bt_keys *oldest = &key_pool[0];
for (i = 1; i < ARRAY_SIZE(key_pool); i++) {
struct bt_keys *current = &key_pool[i];
if (current->aging_counter < oldest->aging_counter) {
oldest = current;
}
}
bt_unpair(oldest->id, &oldest->addr);
if (!bt_addr_le_cmp(&oldest->addr, BT_ADDR_LE_ANY)) {
first_free_slot = oldest - &key_pool[0];
}
}
#endif /* CONFIG_BT_KEYS_OVERWRITE_OLDEST */
if (first_free_slot < ARRAY_SIZE(key_pool)) {
keys = &key_pool[first_free_slot];
keys->id = id;
bt_addr_le_copy(&keys->addr, addr);
#if IS_ENABLED(CONFIG_BT_KEYS_OVERWRITE_OLDEST)
keys->aging_counter = ++aging_counter_val;
last_keys_updated = keys;
#endif /* CONFIG_BT_KEYS_OVERWRITE_OLDEST */
BT_DBG("created %p for %s", keys, bt_addr_le_str(addr));
return keys;
}
BT_DBG("unable to create keys for %s", bt_addr_le_str(addr));
return NULL;
@ -335,17 +367,35 @@ static int keys_set(const char *name, size_t len_rd, settings_read_cb read_cb,
BT_ERR("Failed to allocate keys for %s", bt_addr_le_str(&addr));
return -ENOMEM;
}
if (len != BT_KEYS_STORAGE_LEN) {
do {
/* Load shorter structure for compatibility with old
* records format with no counter.
*/
if (IS_ENABLED(CONFIG_BT_KEYS_OVERWRITE_OLDEST) &&
len == BT_KEYS_STORAGE_LEN_COMPAT) {
BT_WARN("Keys for %s have no aging counter",
bt_addr_le_str(&addr));
memcpy(keys->storage_start, val, len);
continue;
}
if (len_rd != BT_KEYS_STORAGE_LEN) {
BT_ERR("Invalid key length %zu != %zu", len,
BT_KEYS_STORAGE_LEN);
bt_keys_clear(keys);
return -EINVAL;
BT_ERR("Invalid key length %zu != %zu", len,
BT_KEYS_STORAGE_LEN);
bt_keys_clear(keys);
return -EINVAL;
} while (0);
} else {
memcpy(keys->storage_start, val, len);
}
BT_DBG("Successfully restored keys for %s", bt_addr_le_str(&addr));
#if IS_ENABLED(CONFIG_BT_KEYS_OVERWRITE_OLDEST)
if (aging_counter_val < keys->aging_counter) {
aging_counter_val = keys->aging_counter;
}
#endif /* CONFIG_BT_KEYS_OVERWRITE_OLDEST */
return 0;
}
@ -371,3 +421,29 @@ SETTINGS_STATIC_HANDLER_DEFINE(bt_keys, "bt/keys", NULL, keys_set, keys_commit,
NULL);
#endif /* CONFIG_BT_SETTINGS */
#if IS_ENABLED(CONFIG_BT_KEYS_OVERWRITE_OLDEST)
void bt_keys_update_usage(u8_t id, const bt_addr_le_t *addr)
{
struct bt_keys *keys = bt_keys_find_addr(id, addr);
if (!keys) {
return;
}
if (last_keys_updated == keys) {
return;
}
keys->aging_counter = ++aging_counter_val;
last_keys_updated = keys;
BT_DBG("Aging counter for %s is set to %u", bt_addr_le_str(addr),
keys->aging_counter);
if (IS_ENABLED(CONFIG_BT_KEYS_SAVE_AGING_COUNTER_ON_PAIRING)) {
bt_keys_store(keys);
}
}
#endif /* CONFIG_BT_KEYS_OVERWRITE_OLDEST */

View file

@ -59,6 +59,9 @@ struct bt_keys {
#if !defined(CONFIG_BT_SMP_SC_PAIR_ONLY)
struct bt_ltk slave_ltk;
#endif /* CONFIG_BT_SMP_SC_PAIR_ONLY */
#if (defined(CONFIG_BT_KEYS_OVERWRITE_OLDEST))
u32_t aging_counter;
#endif /* CONFIG_BT_KEYS_OVERWRITE_OLDEST */
};
#define BT_KEYS_STORAGE_LEN (sizeof(struct bt_keys) - \
@ -102,3 +105,8 @@ 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);
/* 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(u8_t id, const bt_addr_le_t *addr);