mirror of https://github.com/ARMmbed/mbed-os.git
466 lines
14 KiB
C++
466 lines
14 KiB
C++
/* mbed Microcontroller Library
|
|
* Copyright (c) 2006-2020 ARM Limited
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#if BLE_SECURITY_DATABASE_KVSTORE
|
|
|
|
#include "KVStoreSecurityDb.h"
|
|
#include "common/ble_trace_helpers.h"
|
|
|
|
#define TRACE_GROUP "BLDB"
|
|
|
|
namespace ble {
|
|
|
|
#if BLE_SECURITY_DATABASE_MAX_ENTRIES > 9
|
|
#error "BLE_SECURITY_DATABASE_MAX_ENTRIES must be only one digit long"
|
|
#endif
|
|
|
|
constexpr uint8_t KVStoreSecurityDb::KVSTORESECURITYDB_VERSION;
|
|
constexpr size_t KVStoreSecurityDb::DB_PREFIX_SIZE;
|
|
constexpr size_t KVStoreSecurityDb::DB_KEY_SIZE;
|
|
constexpr size_t KVStoreSecurityDb::DB_ENTRY_KEY_SIZE;
|
|
constexpr size_t KVStoreSecurityDb::DB_FULL_KEY_SIZE;
|
|
constexpr char KVStoreSecurityDb::DB_PREFIX[DB_PREFIX_SIZE+1];
|
|
|
|
constexpr char KVStoreSecurityDb::DB_ENTRIES[DB_KEY_SIZE];
|
|
|
|
constexpr char KVStoreSecurityDb::DB_ENTRY_PEER_IDENTITY[DB_ENTRY_KEY_SIZE];
|
|
constexpr char KVStoreSecurityDb::DB_ENTRY_LOCAL_KEYS[DB_ENTRY_KEY_SIZE];
|
|
constexpr char KVStoreSecurityDb::DB_ENTRY_PEER_KEYS[DB_ENTRY_KEY_SIZE];
|
|
constexpr char KVStoreSecurityDb::DB_ENTRY_PEER_SIGNING[DB_ENTRY_KEY_SIZE];
|
|
|
|
constexpr char KVStoreSecurityDb::DB_LOCAL_IDENTITY[DB_KEY_SIZE];
|
|
constexpr char KVStoreSecurityDb::DB_LOCAL_CSRK[DB_KEY_SIZE];
|
|
constexpr char KVStoreSecurityDb::DB_LOCAL_SIGN_COUNT[DB_KEY_SIZE];
|
|
|
|
constexpr char KVStoreSecurityDb::DB_VERSION[DB_KEY_SIZE];
|
|
constexpr char KVStoreSecurityDb::DB_RESTORE[DB_KEY_SIZE];
|
|
|
|
typedef SecurityDb::entry_handle_t entry_handle_t;
|
|
|
|
KVStoreSecurityDb::KVStoreSecurityDb()
|
|
: SecurityDb() {
|
|
tr_info("KFStore Security DB initialised to store " STR(BLE_SECURITY_DATABASE_MAX_ENTRIES) " entries");
|
|
memset(_entries, 0, sizeof(_entries));
|
|
}
|
|
|
|
KVStoreSecurityDb::~KVStoreSecurityDb()
|
|
{
|
|
}
|
|
|
|
KVStoreSecurityDb::entry_t *KVStoreSecurityDb::as_entry(entry_handle_t db_handle) {
|
|
entry_t* entry = reinterpret_cast<entry_t*>(db_handle);
|
|
if (!entry) {
|
|
tr_error("Invalid security DB handle used");
|
|
}
|
|
return entry;
|
|
}
|
|
|
|
bool KVStoreSecurityDb::open_db()
|
|
{
|
|
uint8_t version = 0;
|
|
char db_key[DB_FULL_KEY_SIZE];
|
|
create_key(db_key, DB_VERSION);
|
|
size_t size;
|
|
int ret = kv_get(db_key, &version, sizeof(uint8_t), &size);
|
|
|
|
/* kvstore problem (check if it's been successfully initialised before this call) */
|
|
if (ret != MBED_ERROR_ITEM_NOT_FOUND && (ret != MBED_SUCCESS || size != sizeof(uint8_t))) {
|
|
tr_error("Failed to read KV store, check if it's initialised");
|
|
return false;
|
|
}
|
|
|
|
/* wipe the db if it's the wrong version or it doesn't exist */
|
|
if (version != KVSTORESECURITYDB_VERSION) {
|
|
tr_warning("Security missing or invalid, reinit");
|
|
return erase_db();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool KVStoreSecurityDb::erase_db()
|
|
{
|
|
tr_info("Erasing security DB in KV store");
|
|
union zero_t {
|
|
int dummy; /* we need a dummy for initialisation */
|
|
uint8_t buffer[sizeof(SecurityEntryKeys_t)];
|
|
entry_t entries[BLE_SECURITY_DATABASE_MAX_ENTRIES];
|
|
} zero = { 0 };
|
|
memset(&zero, 0, sizeof(zero));
|
|
|
|
/* we zero the database and make sure we can fit all our keys */
|
|
|
|
db_write(&zero.entries, DB_ENTRIES);
|
|
db_write((SecurityEntryIdentity_t*)zero.buffer, DB_LOCAL_IDENTITY);
|
|
db_write((csrk_t*)zero.buffer, DB_LOCAL_CSRK);
|
|
db_write((sign_count_t*)zero.buffer, DB_LOCAL_SIGN_COUNT);
|
|
|
|
bool reload = false;
|
|
db_write(&reload, DB_RESTORE);
|
|
|
|
for (int index = 0; index < BLE_SECURITY_DATABASE_MAX_ENTRIES; ++index) {
|
|
db_write_entry((SecurityEntryKeys_t*)zero.buffer, DB_ENTRY_LOCAL_KEYS, index);
|
|
db_write_entry((SecurityEntryIdentity_t*)zero.buffer, DB_ENTRY_PEER_IDENTITY, index);
|
|
db_write_entry((SecurityEntryKeys_t*)zero.buffer, DB_ENTRY_PEER_KEYS, index);
|
|
db_write_entry((SecurityEntrySigning_t*)zero.buffer, DB_ENTRY_PEER_SIGNING, index);
|
|
}
|
|
|
|
/* now we write the version and read it back to see if was written succesfully */
|
|
uint8_t version = KVSTORESECURITYDB_VERSION;
|
|
db_write(&version, DB_VERSION);
|
|
version = 0;
|
|
db_read(&version, DB_VERSION);
|
|
|
|
bool success = (version == KVSTORESECURITYDB_VERSION);
|
|
if (!success) {
|
|
tr_error("failed to write security DB in KV store");
|
|
}
|
|
return success;
|
|
}
|
|
|
|
SecurityDistributionFlags_t* KVStoreSecurityDb::get_distribution_flags(
|
|
entry_handle_t db_handle
|
|
)
|
|
{
|
|
return reinterpret_cast<SecurityDistributionFlags_t*>(db_handle);
|
|
}
|
|
|
|
/* local keys */
|
|
|
|
/* set */
|
|
void KVStoreSecurityDb::set_entry_local_ltk(
|
|
entry_handle_t db_handle,
|
|
const ltk_t <k
|
|
)
|
|
{
|
|
entry_t *entry = as_entry(db_handle);
|
|
if (!entry) {
|
|
return;
|
|
}
|
|
|
|
entry->flags.ltk_sent = true;
|
|
|
|
SecurityEntryKeys_t* current_entry = read_in_entry_local_keys(db_handle);
|
|
current_entry->ltk = ltk;
|
|
|
|
tr_info("Write DB entry %d: local ltk %s", get_index(entry), to_string(ltk));
|
|
db_write_entry(current_entry, DB_ENTRY_LOCAL_KEYS, get_index(entry));
|
|
}
|
|
|
|
void KVStoreSecurityDb::set_entry_local_ediv_rand(
|
|
entry_handle_t db_handle,
|
|
const ediv_t &ediv,
|
|
const rand_t &rand
|
|
)
|
|
{
|
|
entry_t *entry = as_entry(db_handle);
|
|
if (!entry) {
|
|
return;
|
|
}
|
|
|
|
SecurityEntryKeys_t* current_entry = read_in_entry_local_keys(db_handle);
|
|
current_entry->ediv = ediv;
|
|
current_entry->rand = rand;
|
|
|
|
tr_info("Write DB entry %d: local ediv %s rand %s", get_index(entry), to_string(ediv), to_string(rand));
|
|
db_write_entry(current_entry, DB_ENTRY_LOCAL_KEYS, get_index(entry));
|
|
}
|
|
|
|
/* peer's keys */
|
|
|
|
/* set */
|
|
|
|
void KVStoreSecurityDb::set_entry_peer_ltk(
|
|
entry_handle_t db_handle,
|
|
const ltk_t <k
|
|
)
|
|
{
|
|
entry_t *entry = as_entry(db_handle);
|
|
if (!entry) {
|
|
return;
|
|
}
|
|
|
|
entry->flags.ltk_stored = true;
|
|
|
|
SecurityEntryKeys_t* current_entry = read_in_entry_peer_keys(db_handle);
|
|
current_entry->ltk = ltk;
|
|
|
|
tr_info("Write DB entry %d: peer ltk %s", get_index(entry), to_string(ltk));
|
|
db_write_entry(current_entry, DB_ENTRY_PEER_KEYS, get_index(entry));
|
|
}
|
|
|
|
void KVStoreSecurityDb::set_entry_peer_ediv_rand(
|
|
entry_handle_t db_handle,
|
|
const ediv_t &ediv,
|
|
const rand_t &rand
|
|
)
|
|
{
|
|
entry_t *entry = as_entry(db_handle);
|
|
if (!entry) {
|
|
return;
|
|
}
|
|
|
|
SecurityEntryKeys_t* current_entry = read_in_entry_peer_keys(db_handle);
|
|
current_entry->ediv = ediv;
|
|
current_entry->rand = rand;
|
|
|
|
tr_info("Write DB entry %d: peer ediv %s rand %s", get_index(entry), to_string(ediv), to_string(rand));
|
|
db_write_entry(current_entry, DB_ENTRY_PEER_KEYS, get_index(entry));
|
|
}
|
|
|
|
void KVStoreSecurityDb::set_entry_peer_irk(
|
|
entry_handle_t db_handle,
|
|
const irk_t &irk
|
|
)
|
|
{
|
|
entry_t *entry = as_entry(db_handle);
|
|
if (!entry) {
|
|
return;
|
|
}
|
|
|
|
entry->flags.irk_stored = true;
|
|
|
|
SecurityEntryIdentity_t* current_entry = read_in_entry_peer_identity(db_handle);
|
|
current_entry->irk = irk;
|
|
|
|
tr_info("Write DB entry %d: peer irk %s", get_index(entry), to_string(irk));
|
|
db_write_entry(current_entry, DB_ENTRY_PEER_IDENTITY, get_index(entry));
|
|
}
|
|
|
|
void KVStoreSecurityDb::set_entry_peer_bdaddr(
|
|
entry_handle_t db_handle,
|
|
bool address_is_public,
|
|
const address_t &peer_address
|
|
)
|
|
{
|
|
entry_t *entry = as_entry(db_handle);
|
|
if (!entry) {
|
|
return;
|
|
}
|
|
|
|
SecurityEntryIdentity_t* current_entry = read_in_entry_peer_identity(db_handle);
|
|
current_entry->identity_address = peer_address;
|
|
current_entry->identity_address_is_public = address_is_public;
|
|
|
|
tr_info("Write DB entry %d: %s peer address %s", get_index(entry), address_is_public? "public" : "private", to_string(peer_address));
|
|
db_write_entry(current_entry, DB_ENTRY_PEER_IDENTITY, get_index(entry));
|
|
}
|
|
|
|
void KVStoreSecurityDb::set_entry_peer_csrk(
|
|
entry_handle_t db_handle,
|
|
const csrk_t &csrk
|
|
)
|
|
{
|
|
entry_t *entry = as_entry(db_handle);
|
|
if (!entry) {
|
|
return;
|
|
}
|
|
|
|
entry->flags.csrk_stored = true;
|
|
|
|
SecurityEntrySigning_t* current_entry = read_in_entry_peer_signing(db_handle);
|
|
current_entry->csrk = csrk;
|
|
|
|
tr_info("Write DB entry %d: peer csrk %s", get_index(entry), to_string(csrk));
|
|
db_write_entry(current_entry, DB_ENTRY_PEER_SIGNING, get_index(entry));
|
|
}
|
|
|
|
void KVStoreSecurityDb::set_entry_peer_sign_counter(
|
|
entry_handle_t db_handle,
|
|
sign_count_t sign_counter
|
|
)
|
|
{
|
|
entry_t *entry = as_entry(db_handle);
|
|
if (entry) {
|
|
entry->peer_sign_counter = sign_counter;
|
|
}
|
|
}
|
|
|
|
void KVStoreSecurityDb::set_local_csrk(
|
|
const csrk_t &csrk
|
|
)
|
|
{
|
|
this->SecurityDb::set_local_csrk(csrk);
|
|
tr_info("Write DB: local csrk %s", to_string(csrk));
|
|
db_write(&_local_csrk, DB_LOCAL_CSRK);
|
|
}
|
|
|
|
void KVStoreSecurityDb::set_local_identity(
|
|
const irk_t &irk,
|
|
const address_t &identity_address,
|
|
bool public_address
|
|
)
|
|
{
|
|
this->SecurityDb::set_local_identity(irk, identity_address, public_address);
|
|
tr_info("Write DB: %s peer address %s", public_address? "public" : "private", to_string(identity_address));
|
|
db_write(&_local_identity, DB_LOCAL_IDENTITY);
|
|
}
|
|
|
|
/* saving and loading from nvm */
|
|
|
|
void KVStoreSecurityDb::restore()
|
|
{
|
|
/* restore if requested */
|
|
bool restore_toggle = false;
|
|
db_read(&restore_toggle, DB_RESTORE);
|
|
|
|
if (!restore_toggle) {
|
|
erase_db();
|
|
return;
|
|
}
|
|
|
|
tr_info("Valid DB in KV store - restoring security DB");
|
|
db_read(&_entries, DB_ENTRIES);
|
|
db_read(&_local_identity, DB_LOCAL_IDENTITY);
|
|
db_read(&_local_csrk, DB_LOCAL_CSRK);
|
|
db_read(&_local_sign_counter, DB_LOCAL_SIGN_COUNT);
|
|
}
|
|
|
|
void KVStoreSecurityDb::sync(entry_handle_t db_handle)
|
|
{
|
|
/* storage synchronisation is handled by KVStore itself, no explicit flushing */
|
|
|
|
/* if no entry is selected we will sync all entries */
|
|
if (db_handle == invalid_entry_handle) {
|
|
/* only write the connected devices as others are already written */
|
|
for (size_t i = 0; i < get_entry_count(); i++) {
|
|
entry_handle_t db_handle = get_entry_handle_by_index(i);
|
|
SecurityDistributionFlags_t* flags = get_distribution_flags(db_handle);
|
|
|
|
if (flags && flags->connected) {
|
|
sync(db_handle);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
entry_t *entry = as_entry(db_handle);
|
|
if (!entry) {
|
|
return;
|
|
}
|
|
|
|
tr_info("Synchronising security DB with KV store");
|
|
/* all entries are stored in a single key so we store them all*/
|
|
db_write(&_entries, DB_ENTRIES);
|
|
db_write(&_local_identity, DB_LOCAL_IDENTITY);
|
|
db_write(&_local_csrk, DB_LOCAL_CSRK);
|
|
db_write(&_local_sign_counter, DB_LOCAL_SIGN_COUNT);
|
|
}
|
|
|
|
void KVStoreSecurityDb::set_restore(bool reload)
|
|
{
|
|
tr_info("Security DB set to restore on reset: %s", to_string(reload));
|
|
db_write(&reload, DB_RESTORE);
|
|
}
|
|
|
|
/* helper functions */
|
|
|
|
uint8_t KVStoreSecurityDb::get_entry_count()
|
|
{
|
|
return BLE_SECURITY_DATABASE_MAX_ENTRIES;
|
|
}
|
|
|
|
SecurityDistributionFlags_t* KVStoreSecurityDb::get_entry_handle_by_index(uint8_t index)
|
|
{
|
|
if (index < get_entry_count()) {
|
|
return &_entries[index].flags;
|
|
} else {
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
void KVStoreSecurityDb::reset_entry(entry_handle_t db_handle)
|
|
{
|
|
entry_t *entry = as_entry(db_handle);
|
|
if (!entry) {
|
|
return;
|
|
}
|
|
|
|
uint8_t zero_buffer[sizeof(SecurityEntryKeys_t)] = {0};
|
|
|
|
db_write_entry((SecurityEntryKeys_t*)zero_buffer, DB_ENTRY_LOCAL_KEYS, get_index(entry));
|
|
db_write_entry((SecurityEntryIdentity_t*)zero_buffer, DB_ENTRY_PEER_IDENTITY, get_index(entry));
|
|
db_write_entry((SecurityEntryKeys_t*)zero_buffer, DB_ENTRY_PEER_KEYS, get_index(entry));
|
|
db_write_entry((SecurityEntrySigning_t*)zero_buffer, DB_ENTRY_PEER_SIGNING, get_index(entry));
|
|
|
|
entry->flags = SecurityDistributionFlags_t();
|
|
entry->peer_sign_counter = 0;
|
|
}
|
|
|
|
SecurityEntryIdentity_t* KVStoreSecurityDb::read_in_entry_peer_identity(entry_handle_t db_handle)
|
|
{
|
|
entry_t *entry = as_entry(db_handle);
|
|
if (!entry) {
|
|
return nullptr;
|
|
}
|
|
|
|
SecurityEntryIdentity_t* identity = reinterpret_cast<SecurityEntryIdentity_t*>(_buffer);
|
|
db_read_entry(identity, DB_ENTRY_PEER_IDENTITY, get_index(entry));
|
|
|
|
return identity;
|
|
};
|
|
|
|
SecurityEntryKeys_t* KVStoreSecurityDb::read_in_entry_peer_keys(entry_handle_t db_handle)
|
|
{
|
|
entry_t *entry = as_entry(db_handle);
|
|
if (!entry) {
|
|
return nullptr;
|
|
}
|
|
|
|
SecurityEntryKeys_t* keys = reinterpret_cast<SecurityEntryKeys_t*>(_buffer);
|
|
db_read_entry(keys, DB_ENTRY_PEER_KEYS, get_index(entry));
|
|
|
|
return keys;
|
|
};
|
|
|
|
SecurityEntryKeys_t* KVStoreSecurityDb::read_in_entry_local_keys(entry_handle_t db_handle)
|
|
{
|
|
entry_t *entry = as_entry(db_handle);
|
|
if (!entry) {
|
|
return nullptr;
|
|
}
|
|
|
|
SecurityEntryKeys_t* keys = reinterpret_cast<SecurityEntryKeys_t*>(_buffer);
|
|
db_read_entry(keys, DB_ENTRY_LOCAL_KEYS, get_index(entry));
|
|
|
|
return keys;
|
|
};
|
|
|
|
SecurityEntrySigning_t* KVStoreSecurityDb::read_in_entry_peer_signing(entry_handle_t db_handle)
|
|
{
|
|
entry_t *entry = as_entry(db_handle);
|
|
if (!entry) {
|
|
return nullptr;
|
|
}
|
|
|
|
/* only read in the csrk */
|
|
csrk_t* csrk = reinterpret_cast<csrk_t*>(_buffer);
|
|
db_read_entry(csrk, DB_ENTRY_PEER_SIGNING,get_index(entry));
|
|
|
|
|
|
/* use the counter held in memory */
|
|
SecurityEntrySigning_t* signing = reinterpret_cast<SecurityEntrySigning_t*>(_buffer);
|
|
signing->counter = entry->peer_sign_counter;
|
|
|
|
return signing;
|
|
};
|
|
|
|
} /* namespace generic */
|
|
|
|
#endif // BLE_SECURITY_DATABASE_KVSTORE
|