From 25164654dde0226d10c3d7cf31ea92088657d521 Mon Sep 17 00:00:00 2001 From: Sergiy Nikolayenko Date: Thu, 26 Sep 2019 17:27:50 +0300 Subject: [PATCH] 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 --- samples/bluetooth/peripheral/prj.conf | 1 + subsys/bluetooth/host/Kconfig | 15 +++++ subsys/bluetooth/host/conn.c | 5 ++ subsys/bluetooth/host/keys.c | 90 ++++++++++++++++++++++++--- subsys/bluetooth/host/keys.h | 8 +++ 5 files changed, 112 insertions(+), 7 deletions(-) diff --git a/samples/bluetooth/peripheral/prj.conf b/samples/bluetooth/peripheral/prj.conf index 2632d13fb366e4..d31cf9e3029679 100644 --- a/samples/bluetooth/peripheral/prj.conf +++ b/samples/bluetooth/peripheral/prj.conf @@ -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 diff --git a/subsys/bluetooth/host/Kconfig b/subsys/bluetooth/host/Kconfig index 0d5b75476c3c5c..4b832f59d3fb37 100644 --- a/subsys/bluetooth/host/Kconfig +++ b/subsys/bluetooth/host/Kconfig @@ -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" diff --git a/subsys/bluetooth/host/conn.c b/subsys/bluetooth/host/conn.c index b876af44849fe6..e9c0c6010cef5a 100644 --- a/subsys/bluetooth/host/conn.c +++ b/subsys/bluetooth/host/conn.c @@ -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) diff --git a/subsys/bluetooth/host/keys.c b/subsys/bluetooth/host/keys.c index bdd8a0712d1a90..7d767b05d48771 100644 --- a/subsys/bluetooth/host/keys.c +++ b/subsys/bluetooth/host/keys.c @@ -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_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; } @@ -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 */ diff --git a/subsys/bluetooth/host/keys.h b/subsys/bluetooth/host/keys.h index 7b08e6b9c6af04..d6284d0c688134 100644 --- a/subsys/bluetooth/host/keys.h +++ b/subsys/bluetooth/host/keys.h @@ -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);