diff --git a/features/FEATURE_BLE/ble/SecurityManager.h b/features/FEATURE_BLE/ble/SecurityManager.h index 461858e66f..badc09154c 100644 --- a/features/FEATURE_BLE/ble/SecurityManager.h +++ b/features/FEATURE_BLE/ble/SecurityManager.h @@ -428,7 +428,7 @@ public: SecurityIOCapabilities_t iocaps = IO_CAPS_NONE, const Passkey_t passkey = NULL, bool signing = true, - const uint8_t *dbPath = NULL) { + const char *dbPath = NULL) { /* Avoid compiler warnings about unused variables. */ (void)enableBonding; (void)requireMITM; diff --git a/features/FEATURE_BLE/ble/generic/FileSecurityDb.h b/features/FEATURE_BLE/ble/generic/FileSecurityDb.h new file mode 100644 index 0000000000..d345b694db --- /dev/null +++ b/features/FEATURE_BLE/ble/generic/FileSecurityDb.h @@ -0,0 +1,139 @@ +/* mbed Microcontroller Library + * Copyright (c) 2018 ARM Limited + * + * 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. + */ + +#ifndef GENERIC_FILE_SECURITY_DB_H_ +#define GENERIC_FILE_SECURITY_DB_H_ + +#include "SecurityDb.h" + +#include + +namespace ble { +namespace generic { + +/** Filesystem implementation */ +class FileSecurityDb : public SecurityDb { +private: + + struct entry_t { + SecurityDistributionFlags_t flags; + sign_count_t peer_sign_counter; + size_t file_offset; + }; + + static const size_t MAX_ENTRIES = 5; + + static entry_t* as_entry(entry_handle_t db_handle) + { + return reinterpret_cast(db_handle); + } + +public: + FileSecurityDb(FILE *db_file); + virtual ~FileSecurityDb(); + + /** + * Validates or creates a file for the security database. + * @param db_path path to the file + * @return FILE handle open and ready for use by the database or NULL if unavailable + */ + static FILE* open_db_file(const char *db_path); + + virtual SecurityDistributionFlags_t* get_distribution_flags( + entry_handle_t db_handle + ); + + + /* local keys */ + + /* set */ + virtual void set_entry_local_ltk( + entry_handle_t db_handle, + const ltk_t <k + ); + + virtual void set_entry_local_ediv_rand( + entry_handle_t db_handle, + const ediv_t &ediv, + const rand_t &rand + ); + + /* peer's keys */ + + /* set */ + + virtual void set_entry_peer_ltk( + entry_handle_t db_handle, + const ltk_t <k + ); + + virtual void set_entry_peer_ediv_rand( + entry_handle_t db_handle, + const ediv_t &ediv, + const rand_t &rand + ); + + virtual void set_entry_peer_irk( + entry_handle_t db_handle, + const irk_t &irk + ); + + virtual void set_entry_peer_bdaddr( + entry_handle_t db_handle, + bool address_is_public, + const address_t &peer_address + ); + + virtual void set_entry_peer_csrk( + entry_handle_t db_handle, + const csrk_t &csrk + ); + + virtual void set_entry_peer_sign_counter( + entry_handle_t db_handle, + sign_count_t sign_counter + ); + + /* saving and loading from nvm */ + + virtual void restore(); + + virtual void sync(); + + virtual void set_restore(bool reload); + +private: + virtual uint8_t get_entry_count(); + + virtual SecurityDistributionFlags_t* get_entry_handle_by_index(uint8_t index); + + virtual void reset_entry(entry_handle_t db_handle); + + virtual SecurityEntryIdentity_t* read_in_entry_peer_identity(entry_handle_t db_handle); + virtual SecurityEntryKeys_t* read_in_entry_peer_keys(entry_handle_t db_handle); + virtual SecurityEntryKeys_t* read_in_entry_local_keys(entry_handle_t db_handle); + virtual SecurityEntrySigning_t* read_in_entry_peer_signing(entry_handle_t db_handle); + +private: + entry_t _entries[MAX_ENTRIES]; + FILE *_db_file; + static uint8_t _buffer[sizeof(SecurityEntryKeys_t)]; +}; + +} /* namespace pal */ +} /* namespace ble */ + +#endif /*GENERIC_FILE_SECURITY_DB_H_*/ diff --git a/features/FEATURE_BLE/ble/generic/GenericSecurityManager.h b/features/FEATURE_BLE/ble/generic/GenericSecurityManager.h index cb26cc139f..4db0911fb7 100644 --- a/features/FEATURE_BLE/ble/generic/GenericSecurityManager.h +++ b/features/FEATURE_BLE/ble/generic/GenericSecurityManager.h @@ -50,7 +50,7 @@ public: SecurityIOCapabilities_t iocaps = IO_CAPS_NONE, const Passkey_t passkey = NULL, bool signing = true, - const uint8_t* db_path = NULL + const char* db_path = NULL ); virtual ble_error_t reset(); @@ -329,24 +329,22 @@ private: * Returns the CSRK for the connection. Called by the security db. * * @param[in] connectionHandle Handle to identify the connection. - * @param[in] csrk connection signature resolving key. + * @param[in] signing connection signature resolving key and counter. */ void return_csrk_cb( SecurityDb::entry_handle_t connection, - const csrk_t *csrk, - sign_count_t sign_counter + const SecurityEntrySigning_t *signing ); /** * Set the peer CSRK for the connection. Called by the security db. * * @param[in] connectionHandle Handle to identify the connection. - * @param[in] csrk connection signature resolving key. + * @param[in] signing connection signature resolving key and counter. */ void set_peer_csrk_cb( SecurityDb::entry_handle_t connection, - const csrk_t *csrk, - sign_count_t sign_counter + const SecurityEntrySigning_t *signing ); /** diff --git a/features/FEATURE_BLE/ble/generic/MemorySecurityDb.h b/features/FEATURE_BLE/ble/generic/MemorySecurityDb.h index f33b4f9f58..f1ea0b2534 100644 --- a/features/FEATURE_BLE/ble/generic/MemorySecurityDb.h +++ b/features/FEATURE_BLE/ble/generic/MemorySecurityDb.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef PAL_MEMORY_SECURITY_DB_H_ -#define PAL_MEMORY_SECURITY_DB_H_ +#ifndef GENERIC_MEMORY_SECURITY_DB_H_ +#define GENERIC_MEMORY_SECURITY_DB_H_ #include "SecurityDb.h" @@ -25,121 +25,54 @@ namespace generic { /** Naive memory implementation for verification. */ class MemorySecurityDb : public SecurityDb { private: - enum state_t { - ENTRY_FREE, - ENTRY_RESERVED, - ENTRY_WRITTEN, - ENTRY_DISCONNECTED + struct entry_t { + entry_t() : peer_sign_counter(0) { }; + SecurityDistributionFlags_t flags; + SecurityEntryKeys_t local_keys; + SecurityEntryKeys_t peer_keys; + SecurityEntryIdentity_t peer_identity; + SecurityEntrySigning_t peer_signing; + sign_count_t peer_sign_counter; }; - struct entry_t { - entry_t() : sign_counter(0), state(ENTRY_FREE) { }; - SecurityDistributionFlags_t flags; - SecurityEntryKeys_t peer_keys; - SecurityEntryKeys_t local_keys; - SecurityEntryIdentity_t peer_identity; - csrk_t csrk; - sign_count_t sign_counter; - state_t state; - }; static const size_t MAX_ENTRIES = 5; - static entry_t* as_entry(entry_handle_t entry_handle) + static entry_t* as_entry(entry_handle_t db_handle) { - return reinterpret_cast(entry_handle); + return reinterpret_cast(db_handle); } public: - MemorySecurityDb() : _local_sign_counter(0) { } + MemorySecurityDb() : SecurityDb() { } virtual ~MemorySecurityDb() { } - virtual const SecurityDistributionFlags_t* get_distribution_flags( - entry_handle_t entry_handle + virtual SecurityDistributionFlags_t* get_distribution_flags( + entry_handle_t db_handle ) { - entry_t* entry = as_entry(entry_handle); - if (!entry) { - return NULL; - } - - return &entry->flags; - } - - /** - * Set the distribution flags of the DB entry - */ - virtual void set_distribution_flags( - entry_handle_t entry_handle, - const SecurityDistributionFlags_t& flags - ) { - entry_t* entry = as_entry(entry_handle); - if (!entry) { - return; - } - - entry->state = ENTRY_WRITTEN; - entry->flags = flags; + return reinterpret_cast(db_handle); } /* local keys */ - /* get */ - virtual void get_entry_local_keys( - SecurityEntryKeysDbCb_t cb, - entry_handle_t entry_handle, - const ediv_t &ediv, - const rand_t &rand - ) { - entry_t* entry = as_entry(entry_handle); - if (!entry) { - return; - } - - /* validate we have the correct key */ - if (ediv == entry->local_keys.ediv && rand == entry->local_keys.rand) { - cb(entry_handle, &entry->local_keys); - } else { - cb(entry_handle, NULL); - } - } - - virtual void get_entry_local_keys( - SecurityEntryKeysDbCb_t cb, - entry_handle_t entry_handle - ) { - entry_t* entry = as_entry(entry_handle); - if (!entry) { - return; - } - - /* validate we have the correct key */ - if (entry->flags.secure_connections_paired) { - cb(entry_handle, &entry->local_keys); - } else { - cb(entry_handle, NULL); - } - } - - /* set */ virtual void set_entry_local_ltk( - entry_handle_t entry_handle, + entry_handle_t db_handle, const ltk_t <k ) { - entry_t *entry = as_entry(entry_handle); + entry_t *entry = as_entry(db_handle); if (entry) { - entry->state = ENTRY_WRITTEN; + entry->flags.ltk_sent = true; entry->local_keys.ltk = ltk; } } virtual void set_entry_local_ediv_rand( - entry_handle_t entry_handle, + entry_handle_t db_handle, const ediv_t &ediv, const rand_t &rand ) { - entry_t *entry = as_entry(entry_handle); + entry_t *entry = as_entry(db_handle); if (entry) { - entry->state = ENTRY_WRITTEN; entry->local_keys.ediv = ediv; entry->local_keys.rand = rand; } @@ -147,270 +80,118 @@ public: /* peer's keys */ - /* get */ - virtual void get_entry_peer_csrk( - SecurityEntryCsrkDbCb_t cb, - entry_handle_t entry_handle - ) { - csrk_t csrk; - sign_count_t sign_counter = 0; - entry_t *entry = as_entry(entry_handle); - if (entry) { - csrk = entry->csrk; - sign_counter = entry->sign_counter; - } - cb(entry_handle, &csrk, sign_counter); - } - - virtual void get_entry_peer_keys( - SecurityEntryKeysDbCb_t cb, - entry_handle_t entry_handle - ) { - SecurityEntryKeys_t *key = NULL; - entry_t *entry = as_entry(entry_handle); - if (entry) { - key = &entry->peer_keys; - } - cb(entry_handle, key); - } - - virtual void get_entry_identity( - SecurityEntryIdentityDbCb_t cb, - entry_handle_t entry_handle - ) { - entry_t *entry = as_entry(entry_handle); - if (entry && entry->flags.irk_stored) { - cb(entry_handle, &entry->peer_identity); - } else { - cb(entry_handle, NULL); - } - } - - virtual void get_identity_list( - IdentitylistDbCb_t cb, - ArrayView& entries - ) { - size_t count = 0; - for (size_t i = 0; i < MAX_ENTRIES && count < entries.size(); ++i) { - entry_t& e = _entries[i]; - - if (e.state == ENTRY_WRITTEN && e.flags.irk_stored) { - entries[count] = &e.peer_identity; - ++count; - } - } - - cb(entries, count); - } - /* set */ virtual void set_entry_peer_ltk( - entry_handle_t entry_handle, + entry_handle_t db_handle, const ltk_t <k ) { - entry_t *entry = as_entry(entry_handle); + entry_t *entry = as_entry(db_handle); if (entry) { - entry->state = ENTRY_WRITTEN; entry->peer_keys.ltk = ltk; + entry->flags.ltk_stored = true; } } virtual void set_entry_peer_ediv_rand( - entry_handle_t entry_handle, + entry_handle_t db_handle, const ediv_t &ediv, const rand_t &rand ) { - entry_t *entry = as_entry(entry_handle); + entry_t *entry = as_entry(db_handle); if (entry) { - entry->state = ENTRY_WRITTEN; entry->peer_keys.ediv = ediv; entry->peer_keys.rand = rand; } } virtual void set_entry_peer_irk( - entry_handle_t entry_handle, + entry_handle_t db_handle, const irk_t &irk ) { - entry_t *entry = as_entry(entry_handle); + entry_t *entry = as_entry(db_handle); if (entry) { - entry->state = ENTRY_WRITTEN; entry->peer_identity.irk = irk; entry->flags.irk_stored = true; } } virtual void set_entry_peer_bdaddr( - entry_handle_t entry_handle, + entry_handle_t db_handle, bool address_is_public, const address_t &peer_address ) { - entry_t *entry = as_entry(entry_handle); + entry_t *entry = as_entry(db_handle); if (entry) { - entry->state = ENTRY_WRITTEN; entry->peer_identity.identity_address = peer_address; entry->peer_identity.identity_address_is_public = address_is_public; } } virtual void set_entry_peer_csrk( - entry_handle_t entry_handle, + entry_handle_t db_handle, const csrk_t &csrk ) { - entry_t *entry = as_entry(entry_handle); + entry_t *entry = as_entry(db_handle); if (entry) { - entry->state = ENTRY_WRITTEN; - entry->csrk = csrk; + entry->flags.csrk_stored = true; + entry->peer_signing.csrk = csrk; } } virtual void set_entry_peer_sign_counter( - entry_handle_t entry_handle, + entry_handle_t db_handle, sign_count_t sign_counter ) { - entry_t *entry = as_entry(entry_handle); + entry_t *entry = as_entry(db_handle); if (entry) { - entry->state = ENTRY_WRITTEN; - entry->sign_counter = sign_counter; + entry->peer_signing.counter = sign_counter; } } - /* local csrk */ - - virtual const csrk_t* get_local_csrk() { - return &_local_csrk; - } - - virtual void set_local_csrk(const csrk_t &csrk) { - _local_csrk = csrk; - } - - virtual sign_count_t get_local_sign_counter() { - return _local_sign_counter; - } - - virtual void set_local_sign_counter( - sign_count_t sign_counter - ) { - _local_sign_counter = sign_counter; - } - - /* list management */ - - virtual void close_entry(entry_handle_t entry_handle) { - entry_t *entry = as_entry(entry_handle); - if (entry) { - if (entry->state == ENTRY_RESERVED) { - entry->state = ENTRY_FREE; - } else { - entry->state = ENTRY_DISCONNECTED; - } - } - } - - virtual void remove_entry(const address_t peer_identity_address) { - for (size_t i = 0; i < MAX_ENTRIES; i++) { - if (_entries[i].state == ENTRY_FREE) { - continue; - } else if (peer_identity_address == _entries[i].peer_identity.identity_address) { - _entries[i] = entry_t(); - _entries[i].state = ENTRY_FREE; - return; - } - } - } - - virtual void clear_entries() { - for (size_t i = 0; i < MAX_ENTRIES; i++) { - _entries[i] = entry_t(); - } - _local_identity = SecurityEntryIdentity_t(); - _local_csrk = csrk_t(); - } - - virtual void get_whitelist(WhitelistDbCb_t cb, ::Gap::Whitelist_t *whitelist) { - /*TODO: fill whitelist*/ - cb(whitelist); - } - - virtual void generate_whitelist_from_bond_table(WhitelistDbCb_t cb, ::Gap::Whitelist_t *whitelist) { - for (size_t i = 0; i < MAX_ENTRIES && i < whitelist->capacity; i++) { - if (_entries[i].flags.peer_address_is_public) { - whitelist->addresses[i].type = BLEProtocol::AddressType::PUBLIC; - } else { - whitelist->addresses[i].type = BLEProtocol::AddressType::RANDOM_STATIC; - } - - memcpy( - whitelist->addresses[i].address, - _entries[i].peer_identity.identity_address.data(), - sizeof(BLEProtocol::AddressBytes_t) - ); - } - - cb(whitelist); - } - - virtual void set_whitelist(const ::Gap::Whitelist_t &whitelist) { }; - - virtual void add_whitelist_entry(const address_t &address) { } - - virtual void remove_whitelist_entry(const address_t &address) { } - - virtual void clear_whitelist() { } - - /* saving and loading from nvm */ - - virtual void restore() { } - - virtual void sync() { } - - virtual void set_restore(bool reload) { } - private: - virtual uint8_t get_stored_entry_number() { + virtual uint8_t get_entry_count() { return MAX_ENTRIES; } - virtual SecurityDistributionFlags_t* get_stored_entry_flags(uint8_t index) { - return &_entries[index % MAX_ENTRIES].flags; - } - - virtual SecurityEntryIdentity_t* get_stored_entry_identity(uint8_t index) { - return &_entries[index % MAX_ENTRIES].peer_identity; - } - - virtual SecurityDistributionFlags_t* get_free_entry_flags() { - /* get a free one if available */ - for (size_t i = 0; i < MAX_ENTRIES; i++) { - if (_entries[i].state == ENTRY_FREE) { - _entries[i] = entry_t(); - _entries[i].state = ENTRY_RESERVED; - return &_entries[i].flags; - } + virtual SecurityDistributionFlags_t* get_entry_handle_by_index(uint8_t index) { + if (index < MAX_ENTRIES) { + return &_entries[index].flags; + } else { + return NULL; } - - /* get any disconnected one */ - for (size_t i = 0; i < MAX_ENTRIES; i++) { - if (_entries[i].state == ENTRY_DISCONNECTED) { - _entries[i] = entry_t(); - _entries[i].state = ENTRY_RESERVED; - return &_entries[i].flags; - } - } - - return NULL; } + virtual void reset_entry(entry_handle_t db_entry) { + entry_t *entry = reinterpret_cast(db_entry); + *entry = entry_t(); + } + + virtual SecurityEntryIdentity_t* read_in_entry_peer_identity(entry_handle_t db_entry) { + entry_t *entry = reinterpret_cast(db_entry); + return &entry->peer_identity; + }; + + virtual SecurityEntryKeys_t* read_in_entry_peer_keys(entry_handle_t db_entry) { + entry_t *entry = reinterpret_cast(db_entry); + return &entry->peer_keys; + }; + + virtual SecurityEntryKeys_t* read_in_entry_local_keys(entry_handle_t db_entry) { + entry_t *entry = reinterpret_cast(db_entry); + return &entry->local_keys; + }; + + virtual SecurityEntrySigning_t* read_in_entry_peer_signing(entry_handle_t db_entry) { + entry_t *entry = reinterpret_cast(db_entry); + return &entry->peer_signing; + }; + private: entry_t _entries[MAX_ENTRIES]; - SecurityEntryIdentity_t _local_identity; - csrk_t _local_csrk; - sign_count_t _local_sign_counter; }; } /* namespace pal */ } /* namespace ble */ -#endif /*PAL_MEMORY_SECURITY_DB_H_*/ +#endif /*GENERIC_MEMORY_SECURITY_DB_H_*/ diff --git a/features/FEATURE_BLE/ble/generic/SecurityDb.h b/features/FEATURE_BLE/ble/generic/SecurityDb.h index c76bae52bc..ce5115fdc3 100644 --- a/features/FEATURE_BLE/ble/generic/SecurityDb.h +++ b/features/FEATURE_BLE/ble/generic/SecurityDb.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef PAL_SECURITY_MANAGER_DB_H__ -#define PAL_SECURITY_MANAGER_DB_H__ +#ifndef GENERIC_SECURITY_MANAGER_DB_H__ +#define GENERIC_SECURITY_MANAGER_DB_H__ #include "platform/Callback.h" #include "ble/pal/GapTypes.h" @@ -36,11 +36,15 @@ struct SecurityDistributionFlags_t { encryption_key_size(0), peer_address_is_public(false), csrk_stored(false), - csrk_mitm_protected(false), + csrk_sent(false), ltk_stored(false), + ltk_sent(false), + irk_stored(false), + irk_sent(false), + csrk_mitm_protected(false), ltk_mitm_protected(false), secure_connections_paired(false), - irk_stored(false) { + connected(false) { } /** peer address */ @@ -53,16 +57,21 @@ struct SecurityDistributionFlags_t { /** CSRK (Connection Signature Resolving Key) has been distributed and stored */ uint8_t csrk_stored:1; - /** CSRK that is stored has MITM protection */ - uint8_t csrk_mitm_protected:1; + uint8_t csrk_sent:1; /** LTK (Long Term Key) has been distributed and stored */ uint8_t ltk_stored:1; + uint8_t ltk_sent:1; + /** the security entry has been distributed and stored */ + uint8_t irk_stored:1; + uint8_t irk_sent:1; + + /** CSRK that is stored has MITM protection */ + uint8_t csrk_mitm_protected:1; /** LTK that is stored has MITM protection */ uint8_t ltk_mitm_protected:1; /** the current pairing was done using Secure Connections */ uint8_t secure_connections_paired:1; - /** the security entry has been distributed and stored */ - uint8_t irk_stored:1; + uint8_t connected:1; }; /** Long Term Key and data used to identify it */ @@ -75,6 +84,14 @@ struct SecurityEntryKeys_t { rand_t rand; }; +/** CSRK and sign counter used to verify messages */ +struct SecurityEntrySigning_t { + /** Signing key */ + csrk_t csrk; + /** counter used to verify message to guard from replay attacks */ + sign_count_t counter; +}; + /** Data for resolving random resolvable addresses */ struct SecurityEntryIdentity_t { /** identity address */ @@ -103,8 +120,8 @@ public: typedef mbed::Callback SecurityEntryKeysDbCb_t; - typedef mbed::Callback - SecurityEntryCsrkDbCb_t; + typedef mbed::Callback + SecurityEntrySigningDbCb_t; typedef mbed::Callback SecurityEntryIdentityDbCb_t; typedef mbed::Callback&, size_t count)> @@ -112,30 +129,35 @@ public: typedef mbed::Callback WhitelistDbCb_t; - SecurityDb() { }; + SecurityDb() : _local_sign_counter(0) { }; virtual ~SecurityDb() { }; /** * Return immediately security flags associated to a db entry. * - * @param[in] db_entry Entry of the database queried. + * @param[in] db_handle Entry of the database queried. * @return pointer to the flags or NULL if the entry do not have any * associated flags. */ - virtual const SecurityDistributionFlags_t* get_distribution_flags( - entry_handle_t db_entry + virtual SecurityDistributionFlags_t* get_distribution_flags( + entry_handle_t db_handle ) = 0; /** * Set the distribution flags of a DB entry. * - * @param[in] db_entry Entry of the database that will store the flags. - * @param[in] flags Distribution flags to store in @p db_entry. + * @param[in] db_handle Entry of the database that will store the flags. + * @param[in] flags Distribution flags to store in @p db_handle. */ virtual void set_distribution_flags( - entry_handle_t db_entry, - const SecurityDistributionFlags_t& flags - ) = 0; + entry_handle_t db_handle, + const SecurityDistributionFlags_t& new_flags + ) { + SecurityDistributionFlags_t* flags = get_distribution_flags(db_handle); + if (flags) { + *flags = new_flags; + } + } /* local keys */ @@ -143,49 +165,66 @@ public: * Retrieve stored LTK based on passed in EDIV and RAND values. * * @param[in] cb callback that will receive the LTK struct - * @param[in] db_entry handle of the entry being queried. + * @param[in] db_handle handle of the entry being queried. * @param[in] ediv one of the values used to identify the LTK * @param[in] rand one of the values used to identify the LTK */ virtual void get_entry_local_keys( SecurityEntryKeysDbCb_t cb, - entry_handle_t db_entry, + entry_handle_t db_handle, const ediv_t &ediv, const rand_t &rand - ) = 0; + ) { + SecurityEntryKeys_t* keys = read_in_entry_local_keys(db_handle); + /* validate we have the correct key */ + if (keys && ediv == keys->ediv && rand == keys->rand) { + cb(db_handle, keys); + } else { + cb(db_handle, NULL); + } + } /** * Retrieve stored LTK generated during secure connections pairing. * * @param[in] cb callback that will receive the LTK struct - * @param[in] db_entry handle of the entry being queried. + * @param[in] db_handle handle of the entry being queried. */ virtual void get_entry_local_keys( SecurityEntryKeysDbCb_t cb, - entry_handle_t db_entry - ) = 0; + entry_handle_t db_handle + ) { + SecurityEntryKeys_t* keys = read_in_entry_local_keys(db_handle); + SecurityDistributionFlags_t* flags = get_distribution_flags(db_handle); + /* validate we have the correct key */ + if (flags && keys && flags->secure_connections_paired) { + cb(db_handle, keys); + } else { + cb(db_handle, NULL); + } + } /** * Save new local LTK for a connection. * - * @param[in] db_entry handle of the entry being updated. + * @param[in] db_handle handle of the entry being updated. * @param[in] ltk the new LTK, if the device is slave, this is the LTK that * will be used when link is encrypted */ virtual void set_entry_local_ltk( - entry_handle_t db_entry, + entry_handle_t db_handle, const ltk_t <k ) = 0; /** * Update EDIV and RAND used to identify the LTK. * - * @param[in] db_entry handle of the entry being updated. + * @param[in] db_handle handle of the entry being updated. * @param[in] ediv new EDIV value * @param[in] rand new RAND value */ virtual void set_entry_local_ediv_rand( - entry_handle_t db_entry, + entry_handle_t db_handle, const ediv_t &ediv, const rand_t &rand ) = 0; @@ -197,46 +236,52 @@ public: * so that signed packets can be verified. * * @param[in] cb callback which will receive the key - * @param[in] db_entry handle of the entry being queried. + * @param[in] db_handle handle of the entry being queried. */ virtual void get_entry_peer_csrk( - SecurityEntryCsrkDbCb_t cb, - entry_handle_t db_entry - ) = 0; + SecurityEntrySigningDbCb_t cb, + entry_handle_t db_handle + ) { + SecurityEntrySigning_t* signing = read_in_entry_peer_signing(db_handle); + cb(db_handle, signing); + } /** * Return asynchronously the peer encryption key through a callback * so that encryption can be enabled. * * @param[in] cb callback which will receive the key - * @param[in] db_entry handle of the entry being queried. + * @param[in] db_handle handle of the entry being queried. */ virtual void get_entry_peer_keys( SecurityEntryKeysDbCb_t cb, - entry_handle_t db_entry - ) = 0; + entry_handle_t db_handle + ) { + SecurityEntryKeys_t* keys = read_in_entry_peer_keys(db_handle); + cb(db_handle, keys); + } /** * Save new LTK received from the peer. * - * @param[in] db_entry handle of the entry being updated. + * @param[in] db_handle handle of the entry being updated. * @param[in] ltk the new LTK, if the peer device is slave, this is the LTK * that will be used when link is encrypted */ virtual void set_entry_peer_ltk( - entry_handle_t db_entry, + entry_handle_t db_handle, const ltk_t <k ) = 0; /** * Update EDIV and RAND used to identify the LTK sent by the peer. * - * @param[in] db_entry handle of the entry being updated. + * @param[in] db_handle handle of the entry being updated. * @param[in] ediv new EDIV value * @param[in] rand new RAND value */ virtual void set_entry_peer_ediv_rand( - entry_handle_t db_entry, + entry_handle_t db_handle, const ediv_t &ediv, const rand_t &rand ) = 0; @@ -244,23 +289,23 @@ public: /** * Update IRK for this connection. * - * @param[in] db_entry handle of the entry being updated. + * @param[in] db_handle handle of the entry being updated. * @param[in] irk new IRK value */ virtual void set_entry_peer_irk( - entry_handle_t db_entry, + entry_handle_t db_handle, const irk_t &irk ) = 0; /** * Update the identity address of the peer. * - * @param[in] db_entry handle of the entry being updated. + * @param[in] db_handle handle of the entry being updated. * @param[in] address_is_public is the identity address public or private * @param[in] peer_address the new address */ virtual void set_entry_peer_bdaddr( - entry_handle_t db_entry, + entry_handle_t db_handle, bool address_is_public, const address_t &peer_address ) = 0; @@ -269,12 +314,23 @@ public: * Retrieve stored identity address and IRK. * * @param[in] cb callback that will receive the SecurityEntryIdentity_t struct - * @param[in] db_entry handle of the entry being queried. + * @param[in] db_handle handle of the entry being queried. */ virtual void get_entry_identity( SecurityEntryIdentityDbCb_t cb, - entry_handle_t db_entry - ) = 0; + entry_handle_t db_handle + ) { + SecurityDistributionFlags_t* flags = get_distribution_flags(db_handle); + if (flags && flags->irk_stored) { + SecurityEntryIdentity_t* peer_identity = read_in_entry_peer_identity(db_handle); + if (peer_identity) { + cb(db_handle, peer_identity); + return; + } + } + /* avoid duplicate else */ + cb(db_handle, NULL); + } /** * Asynchronously return the identity list stored in NVM through a callback. @@ -288,27 +344,45 @@ public: virtual void get_identity_list( IdentitylistDbCb_t cb, ArrayView& identity_list - ) = 0; + ) { + size_t count = 0; + for (size_t i = 0; i < get_entry_count() && count < identity_list.size(); ++i) { + + entry_handle_t db_handle = get_entry_handle_by_index(i); + SecurityDistributionFlags_t* flags = get_distribution_flags(db_handle); + + + if (flags && flags->irk_stored) { + SecurityEntryIdentity_t* peer_identity = read_in_entry_peer_identity(db_handle); + if (peer_identity) { + identity_list[count] = *peer_identity; + count++; + } + } + } + + cb(identity_list, count); + } /** * Update peer signing key. * - * @param[in] db_entry handle of the entry being updated. + * @param[in] db_handle handle of the entry being updated. * @param[in] csrk new CSRK value */ virtual void set_entry_peer_csrk( - entry_handle_t db_entry, + entry_handle_t db_handle, const csrk_t &csrk ) = 0; /** * Update peer signing counter. * - * @param[in] db_entry handle of the entry being updated. + * @param[in] db_handle handle of the entry being updated. * @param[in] sign_counter new signing counter value */ virtual void set_entry_peer_sign_counter( - entry_handle_t db_entry, + entry_handle_t db_handle, sign_count_t sign_counter ) = 0; @@ -319,14 +393,18 @@ public: * * @return pointer to local CSRK */ - virtual const csrk_t* get_local_csrk() = 0; + virtual const csrk_t* get_local_csrk() { + return &_local_csrk; + } /** * Return local signing counter. * * @return signing counter */ - virtual sign_count_t get_local_sign_counter() = 0; + virtual sign_count_t get_local_sign_counter() { + return _local_sign_counter; + } /** * Update local signing key. @@ -335,7 +413,9 @@ public: */ virtual void set_local_csrk( const csrk_t &csrk - ) = 0; + ) { + _local_csrk = csrk; + } /** * Update local signing counter. @@ -344,7 +424,9 @@ public: */ virtual void set_local_sign_counter( sign_count_t sign_counter - ) = 0; + ) { + _local_sign_counter = sign_counter; + } /* list management */ @@ -363,15 +445,50 @@ public: virtual entry_handle_t open_entry( BLEProtocol::AddressType_t peer_address_type, const address_t &peer_address + ) { + entry_handle_t db_handle = find_entry_by_peer_address(peer_address_type, peer_address); + if (db_handle) { + return db_handle; + } + + SecurityDistributionFlags_t* flags = get_free_entry_flags(); + if (flags) { + const bool peer_address_public = + (peer_address_type == BLEProtocol::AddressType::PUBLIC) || + (peer_address_type == BLEProtocol::AddressType::PUBLIC_IDENTITY); + /* we need some address to store, so we store even random ones + * this address will be used as an id, possibly replaced later + * by identity address */ + flags->peer_address = peer_address; + flags->peer_address_is_public = peer_address_public; + return flags; + } + + return NULL; + } + + /** + * Find a database entry based on peer address. + * + * @param[in] peer_address_type type of address + * @param[in] peer_address this address will be used to locate an existing entry. + * + * @return A handle to the entry. + */ + virtual entry_handle_t find_entry_by_peer_address( + BLEProtocol::AddressType_t peer_address_type, + const address_t &peer_address ) { const bool peer_address_public = (peer_address_type == BLEProtocol::AddressType::PUBLIC) || (peer_address_type == BLEProtocol::AddressType::PUBLIC_IDENTITY); - for (size_t i = 0; i < get_stored_entry_number(); i++) { - SecurityDistributionFlags_t* flags = get_stored_entry_flags(i); + 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) { + /* only look among disconnected entries */ + if (flags && !flags->connected) { if (peer_address_type == BLEProtocol::AddressType::PUBLIC_IDENTITY && flags->irk_stored == false) { continue; @@ -385,7 +502,7 @@ public: /* look for the identity address if stored */ if (flags->irk_stored) { - SecurityEntryIdentity_t* identity = get_stored_entry_identity(i); + SecurityEntryIdentity_t* identity = read_in_entry_peer_identity(db_handle); if (identity && identity->identity_address == peer_address && @@ -393,42 +510,55 @@ public: return flags; } } - } } - SecurityDistributionFlags_t* flags = get_free_entry_flags(); - if (flags) { - /* we need some address to store, so we store even random ones - * this address will be used as an id, possibly replaced later - * by identity address */ - flags->peer_address = peer_address; - flags->peer_address_is_public = peer_address_public; - return flags; - } - return NULL; } /** * Close a connection entry. * - * @param[in] db_entry this handle will be freed up from the security db. + * @param[in] db_handle this handle will be freed up from the security db. */ - virtual void close_entry(entry_handle_t db_entry) = 0; + virtual void close_entry(entry_handle_t db_handle) { + SecurityDistributionFlags_t* flags = get_distribution_flags(db_handle); + if (flags) { + flags->connected = false; + } + sync(); + } /** * Remove entry for this peer from NVM. * - * @param[in] peer_identity_address peer address that no longer needs NVM - * storage. + * @param[in] peer_address_type type of address + * @param[in] peer_address this address will be used to locate an existing + * entry. + * + * @return A handle to the entry. */ - virtual void remove_entry(const address_t peer_identity_address) = 0; + virtual void remove_entry( + BLEProtocol::AddressType_t peer_address_type, + const address_t &peer_address + ) { + entry_handle_t db_handle = find_entry_by_peer_address(peer_address_type, peer_address); + if (db_handle) { + reset_entry(db_handle); + } + } /** * Remove all entries from the security DB. */ - virtual void clear_entries() = 0; + virtual void clear_entries() { + for (size_t i = 0; i < get_entry_count(); i++) { + entry_handle_t db_handle = get_entry_handle_by_index(i); + reset_entry(db_handle); + } + _local_identity = SecurityEntryIdentity_t(); + _local_csrk = csrk_t(); + } /** * Asynchronously return the whitelist stored in NVM through a callback. @@ -441,7 +571,10 @@ public: virtual void get_whitelist( WhitelistDbCb_t cb, ::Gap::Whitelist_t *whitelist - ) = 0; + ) { + /*TODO: fill whitelist*/ + cb(whitelist); + } /** * Asynchronously return a whitelist through a callback, generated from the @@ -453,61 +586,127 @@ public: virtual void generate_whitelist_from_bond_table( WhitelistDbCb_t cb, ::Gap::Whitelist_t *whitelist - ) = 0; + ) { + for (size_t i = 0; i < get_entry_count() && i < whitelist->capacity; i++) { + entry_handle_t db_handle = get_entry_handle_by_index(i); + SecurityDistributionFlags_t* flags = get_distribution_flags(db_handle); + + if (!flags) { + continue; + } + + if (flags->peer_address_is_public) { + whitelist->addresses[i].type = BLEProtocol::AddressType::PUBLIC; + } else { + whitelist->addresses[i].type = BLEProtocol::AddressType::RANDOM_STATIC; + } + + SecurityEntryIdentity_t* identity = read_in_entry_peer_identity(db_handle); + if (identity) { + memcpy( + whitelist->addresses[i].address, + identity->identity_address.data(), + sizeof(BLEProtocol::AddressBytes_t) + ); + } + } + + cb(whitelist); + } /** * Update the whitelist stored in NVM by replacing it with new one. * * @param[in] whitelist */ - virtual void set_whitelist(const ::Gap::Whitelist_t &whitelist) = 0; + virtual void set_whitelist(const ::Gap::Whitelist_t &whitelist) { }; /** * Add a new entry to the whitelist in the NVM. * * @param[in] address new whitelist entry */ - virtual void add_whitelist_entry(const address_t &address) = 0; + virtual void add_whitelist_entry(const address_t &address) { }; /** * Remove whitelist entry from NVM. * * @param[in] address entry to be removed */ - virtual void remove_whitelist_entry(const address_t &address) = 0; + virtual void remove_whitelist_entry(const address_t &address) { }; /** *Remove all whitelist entries stored in the NVM. */ - virtual void clear_whitelist() = 0; + virtual void clear_whitelist() { }; /* saving and loading from nvm */ /** * Read values from storage. */ - virtual void restore() = 0; + virtual void restore() { }; /** * Flush all values which might be stored in memory into NVM. */ - virtual void sync() = 0; + virtual void sync() { }; /** * Toggle whether values should be preserved across resets. * * @param[in] reload if true values will be preserved across resets. */ - virtual void set_restore(bool reload) = 0; + virtual void set_restore(bool reload) { }; -protected: - virtual uint8_t get_stored_entry_number() = 0; - virtual SecurityDistributionFlags_t* get_stored_entry_flags(uint8_t index) = 0; - virtual SecurityEntryIdentity_t* get_stored_entry_identity(uint8_t index) = 0; - virtual SecurityDistributionFlags_t* get_free_entry_flags() = 0; +private: + virtual SecurityDistributionFlags_t* get_free_entry_flags() { + /* get a free one if available */ + SecurityDistributionFlags_t* match = NULL; + 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) { + /* we settle for any disconnected if we don't find an empty one */ + match = flags; + if (!flags->csrk_stored + && !flags->csrk_sent + && !flags->ltk_stored + && !flags->ltk_sent + && !flags->irk_stored + && !flags->irk_sent) { + /* empty one found, stop looking*/ + break; + } + } + } + + if (match) { + reset_entry(match); + } + + return match; + } + + virtual uint8_t get_entry_count() = 0; + + virtual SecurityDistributionFlags_t* get_entry_handle_by_index(uint8_t index) = 0; + + virtual void reset_entry(entry_handle_t db_handle) = 0; + + virtual SecurityEntryIdentity_t* read_in_entry_peer_identity(entry_handle_t db_handle) = 0; + virtual SecurityEntryKeys_t* read_in_entry_peer_keys(entry_handle_t db_handle) = 0; + virtual SecurityEntryKeys_t* read_in_entry_local_keys(entry_handle_t db_handle) = 0; + virtual SecurityEntrySigning_t* read_in_entry_peer_signing(entry_handle_t db_handle) = 0; + +private: + SecurityEntryIdentity_t _local_identity; + csrk_t _local_csrk; + sign_count_t _local_sign_counter; }; } /* namespace pal */ } /* namespace ble */ -#endif /*PAL_SECURITY_MANAGER_DB_H__*/ +#endif /*GENERIC_SECURITY_MANAGER_DB_H__*/ diff --git a/features/FEATURE_BLE/source/generic/FileSecurityDb.cpp b/features/FEATURE_BLE/source/generic/FileSecurityDb.cpp new file mode 100644 index 0000000000..f883dfeebf --- /dev/null +++ b/features/FEATURE_BLE/source/generic/FileSecurityDb.cpp @@ -0,0 +1,357 @@ +/* mbed Microcontroller Library + * Copyright (c) 2018 ARM Limited + * + * 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. + */ + +#include "FileSecurityDb.h" + +namespace ble { +namespace generic { + +const uint16_t DB_VERSION = 1; + +#define DB_STORE_OFFSET_LOCAL_KEYS (0) +#define DB_STORE_OFFSET_PEER_KEYS (DB_STORE_OFFSET_LOCAL_KEYS + sizeof(SecurityEntryKeys_t)) +#define DB_STORE_OFFSET_PEER_IDENTITY (DB_STORE_OFFSET_PEER_KEYS + sizeof(SecurityEntryKeys_t)) +#define DB_STORE_OFFSET_PEER_SIGNING (DB_STORE_OFFSET_PEER_IDENTITY + sizeof(SecurityEntryIdentity_t)) +#define DB_SIZE_STORE_PEER_SIGN_COUNT (DB_STORE_OFFSET_PEER_SIGNING + sizeof(SecurityEntrySigning_t)) + +#define DB_STORE_OFFSET_PEER_IDENTITY_ADDRESS (DB_STORE_OFFSET_PEER_IDENTITY) +#define DB_STORE_OFFSET_PEER_IDENTITY_IRK (DB_STORE_OFFSET_PEER_IDENTITY + sizeof(address_t)) +#define DB_STORE_OFFSET_PEER_IDENTITY_ADDRESS_IS_PUBLIC (DB_STORE_OFFSET_PEER_IDENTITY_IRK + sizeof(irk_t)) + +#define DB_STORE_OFFSET_PEER_KEYS_LTK (DB_STORE_OFFSET_PEER_KEYS) +#define DB_STORE_OFFSET_PEER_KEYS_EDIV (DB_STORE_OFFSET_PEER_KEYS_LTK + sizeof(ltk_t)) +#define DB_STORE_OFFSET_PEER_KEYS_RAND (DB_STORE_OFFSET_PEER_KEYS_EDIV + sizeof(ediv_t)) + +#define DB_STORE_OFFSET_LOCAL_KEYS_LTK (DB_STORE_OFFSET_LOCAL_KEYS) +#define DB_STORE_OFFSET_LOCAL_KEYS_EDIV (DB_STORE_OFFSET_LOCAL_KEYS_LTK + sizeof(ltk_t)) +#define DB_STORE_OFFSET_LOCAL_KEYS_RAND (DB_STORE_OFFSET_LOCAL_KEYS_EDIV + sizeof(ediv_t)) + +#define DB_SIZE_STORE \ + (sizeof(SecurityEntryKeys_t) + \ + sizeof(SecurityEntryKeys_t) + \ + sizeof(SecurityEntryIdentity_t) + \ + sizeof(SecurityEntrySigning_t) + \ + sizeof(sign_count_t)) + +/* without the size of the file offset as we don't store it */ +#define DB_SIZE_ENTRY \ + (sizeof(SecurityDistributionFlags_t) + sizeof(sign_count_t)) + +#define DB_SIZE_ENTRIES \ + (FileSecurityDb::MAX_ENTRIES * DB_SIZE_ENTRY) + +#define DB_SIZE_STORES \ + (FileSecurityDb::MAX_ENTRIES * DB_SIZE_STORE) + +#define DB_OFFSET_VERSION (0) +#define DB_OFFSET_RESTORE (DB_OFFSET_VERSION + sizeof(DB_VERSION)) +#define DB_OFFSET_LOCAL_IDENTITY (DB_OFFSET_RESTORE + sizeof(bool)) +#define DB_OFFSET_LOCAL_CSRK (DB_OFFSET_LOCAL_IDENTITY + sizeof(SecurityEntryIdentity_t)) +#define DB_OFFSET_LOCAL_SIGN_COUNT (DB_OFFSET_LOCAL_CSRK + sizeof(csrk_t)) +#define DB_OFFSET_ENTRIES (DB_OFFSET_LOCAL_SIGN_COUNT + sizeof(sign_count_t)) +#define DB_OFFSET_STORES (DB_OFFSET_ENTRIES + DB_SIZE_ENTRIES) +#define DB_OFFSET_MAX (DB_OFFSET_STORES + DB_SIZE_STORES) +/* make size multiple of 4 */ +#define DB_SIZE ((((DB_OFFSET_MAX - 1) / 4) * 4) + 4) + +typedef SecurityDb::entry_handle_t entry_handle_t; + +FileSecurityDb::FileSecurityDb(FILE *db_file) + : SecurityDb(), + _db_file(db_file) { + fseek(_db_file, DB_OFFSET_RESTORE, SEEK_SET); + + /* restore if requested */ + bool restore; + if ((fread(&restore, sizeof(bool), 1, _db_file) == 1) && restore) { + fseek(_db_file, DB_OFFSET_LOCAL_IDENTITY, SEEK_SET); + fread(&_local_identity, sizeof(_local_identity), 1, _db_file); + + fseek(_db_file, DB_OFFSET_LOCAL_CSRK, SEEK_SET); + fread(&_local_csrk, sizeof(_local_csrk), 1, _db_file); + + fseek(_db_file, DB_OFFSET_LOCAL_SIGN_COUNT, SEEK_SET); + fread(&_local_sign_counter, sizeof(_local_sign_counter), 1, _db_file); + + fseek(_db_file, DB_OFFSET_ENTRIES, SEEK_SET); + /* we read the entries partially and fill the offsets ourselves*/ + for (size_t i = 0; i < get_entry_count(); i++) { + fread(&_entries[i], DB_SIZE_ENTRY, 1, _db_file); + } + } + + /* init the offset in entries so they point to file positions */ + for (size_t i = 0; i < get_entry_count(); i++) { + _entries[i].file_offset = DB_OFFSET_STORES + i * DB_SIZE_STORE; + } +} + +FileSecurityDb::~FileSecurityDb() { + fclose(_db_file); +} + +FILE* FileSecurityDb::open_db_file(const char *db_path) { + FILE *db_file = fopen(db_path, "wb+"); + if (db_file) { + /* we will check the db file and if the version or size doesn't match + * what we expect we will blank it */ + bool init = false; + uint16_t version; + + fseek(db_file, DB_OFFSET_VERSION, SEEK_SET); + + if ((fread(&version, sizeof(version), 1, db_file) == 1) && + (version == DB_VERSION)) { + /* version checks out, try the size */ + fseek(db_file, DB_SIZE - 1, SEEK_SET); + /* read one byte and expect to hit EOF */ + if ((fread(&version, 1, 1, db_file) != 1) || !feof(db_file)) { + init = true; + } + } else { + init = true; + } + + if (init) { + fseek(db_file, 0, SEEK_SET); + + /* zero the file */ + const uint32_t zero = 0; + size_t count = DB_SIZE / 4; + while (count--) { + if (fwrite(&zero, sizeof(zero), 1, db_file) != 1) { + fclose(db_file); + return NULL; + } + } + + if (fflush(db_file)) { + fclose(db_file); + return NULL; + } + } + + return db_file; + } + return NULL; +} + +SecurityDistributionFlags_t* FileSecurityDb::get_distribution_flags( + entry_handle_t db_handle +) { + return reinterpret_cast(db_handle); +} + +/* local keys */ + +/* set */ +void FileSecurityDb::set_entry_local_ltk( + entry_handle_t db_handle, + const ltk_t <k +) { + entry_t *entry = as_entry(db_handle); + if (!entry) { + return; + } + + fseek(_db_file, entry->file_offset + DB_STORE_OFFSET_LOCAL_KEYS_LTK, SEEK_SET); + fwrite(<k, sizeof(ltk_t), 1, _db_file); +} + +void FileSecurityDb::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; + } + + fseek(_db_file, entry->file_offset + DB_STORE_OFFSET_LOCAL_KEYS_EDIV, SEEK_SET); + fwrite(&ediv, sizeof(ediv_t), 1, _db_file); + fseek(_db_file, entry->file_offset + DB_STORE_OFFSET_LOCAL_KEYS_RAND, SEEK_SET); + fwrite(&rand, sizeof(rand_t), 1, _db_file); +} + +/* peer's keys */ + +/* set */ + +void FileSecurityDb::set_entry_peer_ltk( + entry_handle_t db_handle, + const ltk_t <k +) { + entry_t *entry = as_entry(db_handle); + if (!entry) { + return; + } + + fseek(_db_file, entry->file_offset + DB_STORE_OFFSET_PEER_KEYS_LTK, SEEK_SET); + fwrite(<k, sizeof(ltk_t), 1, _db_file); +} + +void FileSecurityDb::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; + } + + fseek(_db_file, entry->file_offset + DB_STORE_OFFSET_PEER_KEYS_EDIV, SEEK_SET); + fwrite(&ediv, sizeof(ediv_t), 1, _db_file); + fseek(_db_file, entry->file_offset + DB_STORE_OFFSET_PEER_KEYS_RAND, SEEK_SET); + fwrite(&rand, sizeof(rand_t), 1, _db_file); +} + +void FileSecurityDb::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; + + fseek(_db_file, entry->file_offset + DB_STORE_OFFSET_PEER_IDENTITY_IRK, SEEK_SET); + fwrite(&irk, sizeof(irk_t), 1, _db_file); +} + +void FileSecurityDb::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; + } + + fseek(_db_file, entry->file_offset + DB_STORE_OFFSET_PEER_IDENTITY_ADDRESS, SEEK_SET); + fwrite(&peer_address, sizeof(address_t), 1, _db_file); + fseek(_db_file, entry->file_offset + DB_STORE_OFFSET_PEER_IDENTITY_ADDRESS_IS_PUBLIC, SEEK_SET); + fwrite(&address_is_public, sizeof(bool), 1, _db_file); +} + +void FileSecurityDb::set_entry_peer_csrk( + entry_handle_t db_handle, + const csrk_t &csrk +) { + entry_t *entry = as_entry(db_handle); + if (!entry) { + return; + } + + fseek(_db_file, entry->file_offset + DB_STORE_OFFSET_PEER_SIGNING, SEEK_SET); + /* only write in the csrk */ + fwrite(&csrk, sizeof(csrk_t), 1, _db_file); +} + +void FileSecurityDb::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; + } +} + +/* saving and loading from nvm */ + +void FileSecurityDb::restore() { +} + +void FileSecurityDb::sync() { +} + +void FileSecurityDb::set_restore(bool reload) { +} + +uint8_t FileSecurityDb::get_entry_count() { + return MAX_ENTRIES; +} + +SecurityDistributionFlags_t* FileSecurityDb::get_entry_handle_by_index(uint8_t index) { + if (index < MAX_ENTRIES) { + return &_entries[index].flags; + } else { + return NULL; + } +} + +void FileSecurityDb::reset_entry(entry_handle_t db_entry) { + entry_t *entry = as_entry(db_entry); + if (!entry) { + return; + } + entry->flags = SecurityDistributionFlags_t(); + entry->peer_sign_counter = 0; +} + +SecurityEntryIdentity_t* FileSecurityDb::read_in_entry_peer_identity(entry_handle_t db_entry) { + entry_t *entry = as_entry(db_entry); + if (!entry) { + return NULL; + } + fseek(_db_file, entry->file_offset + DB_STORE_OFFSET_PEER_IDENTITY, SEEK_SET); + fread(&_buffer, sizeof(SecurityEntryIdentity_t), 1, _db_file); + return reinterpret_cast(_buffer); +}; + +SecurityEntryKeys_t* FileSecurityDb::read_in_entry_peer_keys(entry_handle_t db_entry) { + entry_t *entry = as_entry(db_entry); + if (!entry) { + return NULL; + } + fseek(_db_file, entry->file_offset + DB_STORE_OFFSET_PEER_KEYS, SEEK_SET); + fread(&_buffer, sizeof(SecurityEntryKeys_t), 1, _db_file); + return reinterpret_cast(_buffer); +}; + +SecurityEntryKeys_t* FileSecurityDb::read_in_entry_local_keys(entry_handle_t db_entry) { + entry_t *entry = as_entry(db_entry); + if (!entry) { + return NULL; + } + fseek(_db_file, entry->file_offset + DB_STORE_OFFSET_LOCAL_KEYS, SEEK_SET); + fread(&_buffer, sizeof(SecurityEntryKeys_t), 1, _db_file); + return reinterpret_cast(_buffer); +}; + +SecurityEntrySigning_t* FileSecurityDb::read_in_entry_peer_signing(entry_handle_t db_entry) { + entry_t *entry = as_entry(db_entry); + if (!entry) { + return NULL; + } + fseek(_db_file, entry->file_offset + DB_STORE_OFFSET_PEER_SIGNING, SEEK_SET); + + /* only read in the csrk */ + fread(&_buffer, sizeof(csrk_t), 1, _db_file); + SecurityEntrySigning_t* signing = reinterpret_cast(_buffer); + /* use the counter held in memory */ + signing->counter = entry->peer_sign_counter; + + return signing; +}; + +} /* namespace pal */ +} /* namespace ble */ diff --git a/features/FEATURE_BLE/source/generic/GenericSecurityManager.cpp b/features/FEATURE_BLE/source/generic/GenericSecurityManager.cpp index 51878ef678..7a3a43e389 100644 --- a/features/FEATURE_BLE/source/generic/GenericSecurityManager.cpp +++ b/features/FEATURE_BLE/source/generic/GenericSecurityManager.cpp @@ -40,16 +40,26 @@ ble_error_t GenericSecurityManager::init( SecurityIOCapabilities_t iocaps, const Passkey_t passkey, bool signing, - const uint8_t* db_path + const char* db_path ) { + if (_db) { + return BLE_ERROR_OPERATION_NOT_PERMITTED; + } + ble_error_t err = _pal.initialize(); if (err) { return err; } - _db = new (std::nothrow) MemorySecurityDb(); + FILE* db_file = FileSecurityDb::open_db_file(db_path); + if (db_file) { + _db = new (std::nothrow) FileSecurityDb(db_file); + } else { + _db = new (std::nothrow) MemorySecurityDb(); + } _db->restore(); + _pal.set_io_capability((io_capability_t::type) iocaps); if (passkey) { @@ -99,6 +109,7 @@ ble_error_t GenericSecurityManager::init( } ble_error_t GenericSecurityManager::reset(void) { + MBED_ASSERT(_db); _db->sync(); _pal.reset(); SecurityManager::reset(); @@ -107,6 +118,7 @@ ble_error_t GenericSecurityManager::reset(void) { } ble_error_t GenericSecurityManager::preserveBondingStateOnReset(bool enabled) { + MBED_ASSERT(_db); _db->set_restore(enabled); return BLE_ERROR_NONE; } @@ -116,11 +128,13 @@ ble_error_t GenericSecurityManager::preserveBondingStateOnReset(bool enabled) { // ble_error_t GenericSecurityManager::purgeAllBondingState(void) { + MBED_ASSERT(_db); _db->clear_entries(); return BLE_ERROR_NONE; } ble_error_t GenericSecurityManager::generateWhitelistFromBondTable(Gap::Whitelist_t *whitelist) const { + MBED_ASSERT(_db); if (eventHandler) { _db->generate_whitelist_from_bond_table( mbed::callback(eventHandler, &::SecurityManager::EventHandler::whitelistFromBondTable), @@ -327,6 +341,7 @@ ble_error_t GenericSecurityManager::enableSigning( connection_handle_t connection, bool enabled ) { + MBED_ASSERT(_db); ControlBlock_t *cb = get_control_block(connection); if (!cb) { return BLE_ERROR_INVALID_PARAM; @@ -468,6 +483,7 @@ ble_error_t GenericSecurityManager::setEncryptionKeyRequirements( // ble_error_t GenericSecurityManager::getSigningKey(connection_handle_t connection, bool authenticated) { + MBED_ASSERT(_db); ControlBlock_t *cb = get_control_block(connection); if (!cb) { return BLE_ERROR_INVALID_PARAM; @@ -663,6 +679,7 @@ ble_error_t GenericSecurityManager::oobReceived( // ble_error_t GenericSecurityManager::init_signing() { + MBED_ASSERT(_db); const csrk_t *pcsrk = _db->get_local_csrk(); sign_count_t local_sign_counter = _db->get_local_sign_counter(); @@ -712,6 +729,7 @@ ble_error_t GenericSecurityManager::slave_security_request(connection_handle_t c } ble_error_t GenericSecurityManager::enable_encryption(connection_handle_t connection) { + MBED_ASSERT(_db); ControlBlock_t *cb = get_control_block(connection); if (!cb) { return BLE_ERROR_INVALID_PARAM; @@ -763,35 +781,33 @@ void GenericSecurityManager::set_ltk_cb( void GenericSecurityManager::set_peer_csrk_cb( SecurityDb::entry_handle_t db_entry, - const csrk_t *csrk, - sign_count_t sign_counter + const SecurityEntrySigning_t* signing ) { ControlBlock_t *cb = get_control_block(db_entry); - if (!cb) { + if (!cb || !signing) { return; } _pal.set_peer_csrk( cb->connection, - *csrk, + signing->csrk, cb->csrk_mitm_protected, - sign_counter + signing->counter ); } void GenericSecurityManager::return_csrk_cb( SecurityDb::entry_handle_t db_entry, - const csrk_t *csrk, - sign_count_t sign_counter + const SecurityEntrySigning_t *signing ) { ControlBlock_t *cb = get_control_block(db_entry); - if (!cb) { + if (!cb || !signing) { return; } eventHandler->signingKey( cb->connection, - csrk, + &signing->csrk, cb->csrk_mitm_protected ); } @@ -838,6 +854,7 @@ void GenericSecurityManager::on_connected( const BLEProtocol::AddressBytes_t local_address, const Gap::ConnectionParams_t *connection_params ) { + MBED_ASSERT(_db); ControlBlock_t *cb = acquire_control_block(connection); if (!cb) { return; @@ -876,6 +893,7 @@ void GenericSecurityManager::on_disconnected( connection_handle_t connection, Gap::DisconnectionReason_t reason ) { + MBED_ASSERT(_db); ControlBlock_t *cb = get_control_block(connection); if (!cb) { return; @@ -994,6 +1012,7 @@ void GenericSecurityManager::on_pairing_timed_out(connection_handle_t connection } void GenericSecurityManager::on_pairing_completed(connection_handle_t connection) { + MBED_ASSERT(_db); ControlBlock_t *cb = get_control_block(connection); if (cb) { // set the distribution flags in the db @@ -1022,6 +1041,7 @@ void GenericSecurityManager::on_signed_write_received( connection_handle_t connection, sign_count_t sign_counter ) { + MBED_ASSERT(_db); ControlBlock_t *cb = get_control_block(connection); if (!cb) { return; @@ -1055,6 +1075,7 @@ void GenericSecurityManager::on_signed_write_verification_failure( } void GenericSecurityManager::on_signed_write() { + MBED_ASSERT(_db); _db->set_local_sign_counter(_db->get_local_sign_counter() + 1); } @@ -1226,6 +1247,7 @@ void GenericSecurityManager::on_secure_connections_ltk_generated( connection_handle_t connection, const ltk_t <k ) { + MBED_ASSERT(_db); ControlBlock_t *cb = get_control_block(connection); if (!cb) { return; @@ -1241,6 +1263,7 @@ void GenericSecurityManager::on_keys_distributed_ltk( connection_handle_t connection, const ltk_t <k ) { + MBED_ASSERT(_db); ControlBlock_t *cb = get_control_block(connection); if (!cb) { return; @@ -1254,6 +1277,7 @@ void GenericSecurityManager::on_keys_distributed_ediv_rand( const ediv_t &ediv, const rand_t &rand ) { + MBED_ASSERT(_db); ControlBlock_t *cb = get_control_block(connection); if (!cb) { return; @@ -1266,6 +1290,7 @@ void GenericSecurityManager::on_keys_distributed_local_ltk( connection_handle_t connection, const ltk_t <k ) { + MBED_ASSERT(_db); ControlBlock_t *cb = get_control_block(connection); if (!cb) { return; @@ -1279,6 +1304,7 @@ void GenericSecurityManager::on_keys_distributed_local_ediv_rand( const ediv_t &ediv, const rand_t &rand ) { + MBED_ASSERT(_db); ControlBlock_t *cb = get_control_block(connection); if (!cb) { return; @@ -1291,6 +1317,7 @@ void GenericSecurityManager::on_keys_distributed_irk( connection_handle_t connection, const irk_t &irk ) { + MBED_ASSERT(_db); ControlBlock_t *cb = get_control_block(connection); if (!cb) { return; @@ -1304,6 +1331,7 @@ void GenericSecurityManager::on_keys_distributed_bdaddr( advertising_peer_address_type_t peer_address_type, const address_t &peer_identity_address ) { + MBED_ASSERT(_db); ControlBlock_t *cb = get_control_block(connection); if (!cb) { return; @@ -1320,6 +1348,7 @@ void GenericSecurityManager::on_keys_distributed_csrk( connection_handle_t connection, const csrk_t &csrk ) { + MBED_ASSERT(_db); ControlBlock_t *cb = get_control_block(connection); if (!cb) { return; @@ -1341,6 +1370,7 @@ void GenericSecurityManager::on_ltk_request( const ediv_t &ediv, const rand_t &rand ) { + MBED_ASSERT(_db); ControlBlock_t *cb = get_control_block(connection); if (!cb) { return; @@ -1379,6 +1409,7 @@ GenericSecurityManager::ControlBlock_t::ControlBlock_t() : void GenericSecurityManager::on_ltk_request(connection_handle_t connection) { + MBED_ASSERT(_db); ControlBlock_t *cb = get_control_block(connection); if (!cb) { return;