Skip to content

Commit

Permalink
Bluetooth: Keys: add key overwrite feature for key storage.
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Sergiy Nikolayenko authored and jhedberg committed Oct 17, 2019
1 parent e5d7b2b commit 2516465
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 7 deletions.
1 change: 1 addition & 0 deletions samples/bluetooth/peripheral/prj.conf
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
15 changes: 15 additions & 0 deletions subsys/bluetooth/host/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
5 changes: 5 additions & 0 deletions subsys/bluetooth/host/conn.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
90 changes: 83 additions & 7 deletions subsys/bluetooth/host/keys.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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_rd != BT_KEYS_STORAGE_LEN) {
BT_ERR("Invalid key length %zu != %zu", len,
BT_KEYS_STORAGE_LEN);
bt_keys_clear(keys);
return -EINVAL;
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;
}

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

Expand All @@ -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 */
8 changes: 8 additions & 0 deletions subsys/bluetooth/host/keys.h
Original file line number Diff line number Diff line change
Expand Up @@ -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) - \
Expand Down Expand Up @@ -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);

0 comments on commit 2516465

Please sign in to comment.