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