diff --git a/features/FEATURE_BLE/ble/SecurityManager.h b/features/FEATURE_BLE/ble/SecurityManager.h index 2e6280e80d..56313ec71d 100644 --- a/features/FEATURE_BLE/ble/SecurityManager.h +++ b/features/FEATURE_BLE/ble/SecurityManager.h @@ -285,6 +285,22 @@ public: (void)result; } + /** + * Indicate that a peer address has been saved by the security manager or if we are + * bonded to the peer the identity has been retrieved from the database on connection. + * + * @param[in] connectionHandle Connection handle. + * @param[in] peer_address Peer address that has been saved by the security database, NULL it not found. + * @param[in] address_is_public Address type, true if public. Invalid if peer_address NULL. + */ + virtual void peerIdentity(ble::connection_handle_t connectionHandle, + const address_t *peer_address, + bool address_is_public) { + (void)connectionHandle; + (void)peer_address; + (void)address_is_public; + } + //////////////////////////////////////////////////////////////////////////// // Security // @@ -561,6 +577,14 @@ public: */ ble_error_t setPairingRequestAuthorisation(bool required = true); + /** + * Retrieve identity address for the peer on the given connection. + * + * @param[in] connectionHandle Handle to identify the connection. + * @return BLE_ERROR_NONE or appropriate error code indicating the failure reason. + */ + ble_error_t getPeerIdentity(ble::connection_handle_t connectionHandle); + //////////////////////////////////////////////////////////////////////////// // Feature support // @@ -899,6 +923,10 @@ protected: ble::connection_handle_t connectionHandle ); + ble_error_t getPeerIdentity_( + ble::connection_handle_t connectionHandle + ); + ble_error_t setPairingRequestAuthorisation_( bool required ); diff --git a/features/FEATURE_BLE/ble/generic/FileSecurityDb.h b/features/FEATURE_BLE/ble/generic/FileSecurityDb.h index 4c57d67005..2dcdb05843 100644 --- a/features/FEATURE_BLE/ble/generic/FileSecurityDb.h +++ b/features/FEATURE_BLE/ble/generic/FileSecurityDb.h @@ -34,8 +34,6 @@ private: 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); } @@ -118,6 +116,21 @@ public: sign_count_t sign_counter ); + /* local csrk and identity */ + + virtual void set_local_csrk( + const csrk_t &csrk + ); + + virtual void set_local_identity( + const irk_t &irk, + const address_t &identity_address, + bool public_address + ); + + /* I am not overriding set_local_sign_counter to avoid constant filesystem writes, + * instead this is synced by sync (which is called on disconnection) */ + /* saving and loading from nvm */ virtual void restore(); @@ -146,7 +159,7 @@ private: static FILE* erase_db_file(FILE* db_file); private: - entry_t _entries[MAX_ENTRIES]; + entry_t _entries[BLE_SECURITY_DATABASE_MAX_ENTRIES]; FILE *_db_file; uint8_t _buffer[sizeof(SecurityEntryKeys_t)]; }; diff --git a/features/FEATURE_BLE/ble/generic/GenericSecurityManager.h b/features/FEATURE_BLE/ble/generic/GenericSecurityManager.h index 6f23d078f5..cc94b97b0b 100644 --- a/features/FEATURE_BLE/ble/generic/GenericSecurityManager.h +++ b/features/FEATURE_BLE/ble/generic/GenericSecurityManager.h @@ -124,6 +124,10 @@ public: bool required = true ); + ble_error_t getPeerIdentity_( + connection_handle_t connection + ); + //////////////////////////////////////////////////////////////////////////// // Feature support // @@ -321,6 +325,13 @@ private: */ ble_error_t init_signing(); + /** + * Generate the IRK if needed. + * + * @return BLE_ERROR_NONE or appropriate error code indicating the failure reason. + */ + ble_error_t init_identity(); + /** * Fills the buffer with the specified number of bytes of random data * produced by the link controller diff --git a/features/FEATURE_BLE/ble/generic/MemorySecurityDb.h b/features/FEATURE_BLE/ble/generic/MemorySecurityDb.h index 6ca9c3b3b0..6ae94ed01c 100644 --- a/features/FEATURE_BLE/ble/generic/MemorySecurityDb.h +++ b/features/FEATURE_BLE/ble/generic/MemorySecurityDb.h @@ -34,8 +34,6 @@ private: SecurityEntrySigning_t peer_signing; }; - static const size_t MAX_ENTRIES = 5; - static entry_t* as_entry(entry_handle_t db_handle) { return reinterpret_cast(db_handle); @@ -150,11 +148,11 @@ public: private: virtual uint8_t get_entry_count() { - return MAX_ENTRIES; + return BLE_SECURITY_DATABASE_MAX_ENTRIES; } virtual SecurityDistributionFlags_t* get_entry_handle_by_index(uint8_t index) { - if (index < MAX_ENTRIES) { + if (index < BLE_SECURITY_DATABASE_MAX_ENTRIES) { return &_entries[index].flags; } else { return NULL; @@ -187,7 +185,7 @@ private: }; private: - entry_t _entries[MAX_ENTRIES]; + entry_t _entries[BLE_SECURITY_DATABASE_MAX_ENTRIES]; }; } /* namespace pal */ diff --git a/features/FEATURE_BLE/ble/generic/SecurityDb.h b/features/FEATURE_BLE/ble/generic/SecurityDb.h index 9228dbd39d..358e5224dc 100644 --- a/features/FEATURE_BLE/ble/generic/SecurityDb.h +++ b/features/FEATURE_BLE/ble/generic/SecurityDb.h @@ -425,6 +425,31 @@ public: _local_sign_counter = sign_counter; } + /* local identity */ + /** + * Update the local identity. + * + * @param[in] csrk new CSRK value + */ + virtual void set_local_identity( + const irk_t &irk, + const address_t &identity_address, + bool public_address + ) { + _local_identity.irk = irk; + _local_identity.identity_address = identity_address; + _local_identity.identity_address_is_public = public_address; + } + + /** + * Return local irk. + * + * @return irk + */ + virtual irk_t get_local_irk() { + return _local_identity.irk; + } + /* list management */ /** @@ -592,6 +617,21 @@ public: continue; } + // Add the connection address + whitelist->addresses[whitelist->size].address = flags->peer_address.data(); + + if (flags->peer_address_is_public) { + whitelist->addresses[whitelist->size].type = peer_address_type_t::PUBLIC; + } else { + whitelist->addresses[whitelist->size].type = peer_address_type_t::RANDOM; + } + + whitelist->size++; + if (whitelist->size == whitelist->capacity) { + break; + } + + // Add the identity address SecurityEntryIdentity_t* identity = read_in_entry_peer_identity(db_handle); if (!identity) { continue; diff --git a/features/FEATURE_BLE/ble/pal/PalSecurityManager.h b/features/FEATURE_BLE/ble/pal/PalSecurityManager.h index 85c024f8e6..31d6643209 100644 --- a/features/FEATURE_BLE/ble/pal/PalSecurityManager.h +++ b/features/FEATURE_BLE/ble/pal/PalSecurityManager.h @@ -964,7 +964,23 @@ public: ble_error_t set_private_address_timeout( uint16_t timeout_in_seconds ) { - return impl()->set_private_address_timeout(timeout_in_seconds); + return impl()->set_private_address_timeout_(timeout_in_seconds); + } + + /** + * Retrieve the identity address used by the controller + * + * @param address Will contain the address retrieved. + * @param public_address will be true if the address is public and false + * otherwise. + * @return BLE_ERROR_NONE On success, else an error code indicating the reason + * of the failure + */ + ble_error_t get_identity_address( + address_t& address, + bool& public_address + ) { + return impl()->get_identity_address_(address, public_address); } //////////////////////////////////////////////////////////////////////////// diff --git a/features/FEATURE_BLE/mbed_lib.json b/features/FEATURE_BLE/mbed_lib.json index efae862cad..64601cec01 100644 --- a/features/FEATURE_BLE/mbed_lib.json +++ b/features/FEATURE_BLE/mbed_lib.json @@ -71,6 +71,11 @@ "help": "Include periodic advertising support, depends on the extended advertising feature.", "value": true, "macro_name": "BLE_FEATURE_PERIODIC_ADVERTISING" + }, + "ble-security-database-max-entries": { + "help": "How many entries can be stored in the db, depends on security manager.", + "value": 5, + "macro_name": "BLE_SECURITY_DATABASE_MAX_ENTRIES" } } } \ No newline at end of file diff --git a/features/FEATURE_BLE/source/generic/FileSecurityDb.cpp b/features/FEATURE_BLE/source/generic/FileSecurityDb.cpp index 4b0db269e0..abefb7f209 100644 --- a/features/FEATURE_BLE/source/generic/FileSecurityDb.cpp +++ b/features/FEATURE_BLE/source/generic/FileSecurityDb.cpp @@ -54,7 +54,7 @@ const uint16_t DB_VERSION = 1; ) #define DB_SIZE_STORES \ - (FileSecurityDb::MAX_ENTRIES * DB_SIZE_STORE) + (BLE_SECURITY_DATABASE_MAX_ENTRIES * DB_SIZE_STORE) #define DB_OFFSET_VERSION (0) #define DB_OFFSET_RESTORE (DB_OFFSET_VERSION + sizeof(DB_VERSION)) @@ -265,6 +265,22 @@ void FileSecurityDb::set_entry_peer_sign_counter( } } +void FileSecurityDb::set_local_csrk( + const csrk_t &csrk +) { + this->SecurityDb::set_local_csrk(csrk); + db_write(&_local_csrk, DB_OFFSET_LOCAL_CSRK); +} + +void FileSecurityDb::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); + db_write(&_local_identity, DB_OFFSET_LOCAL_IDENTITY); +} + /* saving and loading from nvm */ void FileSecurityDb::restore() { @@ -299,6 +315,7 @@ void FileSecurityDb::sync(entry_handle_t db_handle) { db_write(&entry->peer_sign_counter, entry->file_offset + DB_STORE_OFFSET_PEER_SIGNING_COUNT); db_write(&entry->flags, entry->file_offset + DB_STORE_OFFSET_FLAGS); + db_write(&_local_sign_counter, DB_OFFSET_LOCAL_SIGN_COUNT); } void FileSecurityDb::set_restore(bool reload) { @@ -308,11 +325,11 @@ void FileSecurityDb::set_restore(bool reload) { /* helper functions */ uint8_t FileSecurityDb::get_entry_count() { - return MAX_ENTRIES; + return BLE_SECURITY_DATABASE_MAX_ENTRIES; } SecurityDistributionFlags_t* FileSecurityDb::get_entry_handle_by_index(uint8_t index) { - if (index < MAX_ENTRIES) { + if (index < BLE_SECURITY_DATABASE_MAX_ENTRIES) { return &_entries[index].flags; } else { return NULL; diff --git a/features/FEATURE_BLE/source/generic/GenericSecurityManager.tpp b/features/FEATURE_BLE/source/generic/GenericSecurityManager.tpp index b18ef4fd44..e08a6f575c 100644 --- a/features/FEATURE_BLE/source/generic/GenericSecurityManager.tpp +++ b/features/FEATURE_BLE/source/generic/GenericSecurityManager.tpp @@ -102,13 +102,19 @@ ble_error_t GenericSecurityManager::init_( result = init_resolving_list(); #endif +#if BLE_FEATURE_PRIVACY + // set the local identity address and irk + if (result != BLE_ERROR_NONE) { + result = init_identity(); + } +#endif // BLE_FEATURE_PRIVACY + if (result != BLE_ERROR_NONE) { delete _db; _db = NULL; - return result; } - return BLE_ERROR_NONE; + return result; } template class TPalSecurityManager, template class SigningMonitor> @@ -161,7 +167,21 @@ template class TPalSecurityManager, template class Signin ble_error_t GenericSecurityManager::purgeAllBondingState_(void) { if (!_db) return BLE_ERROR_INITIALIZATION_INCOMPLETE; _db->clear_entries(); - return BLE_ERROR_NONE; + + ble_error_t ret = BLE_ERROR_NONE; + +#if BLE_FEATURE_SIGNING + // generate new csrk and irk + ret = init_signing(); + if (ret) { + return ret; + } +#endif // BLE_FEATURE_SIGNING +#if BLE_FEATURE_PRIVACY + ret = init_identity(); +#endif // BLE_FEATURE_PRIVACY + + return ret; } template class TPalSecurityManager, template class SigningMonitor> @@ -309,6 +329,33 @@ ble_error_t GenericSecurityManager::setPair return BLE_ERROR_NONE; } +template class TPalSecurityManager, template class SigningMonitor> +ble_error_t GenericSecurityManager::getPeerIdentity_(connection_handle_t connection) { + if (!_db) return BLE_ERROR_INITIALIZATION_INCOMPLETE; + if (eventHandler) { + ControlBlock_t *cb = get_control_block(connection); + if (!cb) { + return BLE_ERROR_INVALID_PARAM; + } + + _db->get_entry_identity( + [connection,this](SecurityDb::entry_handle_t handle, const SecurityEntryIdentity_t* identity) { + if (eventHandler) { + eventHandler->peerIdentity( + connection, + identity ? &identity->identity_address : nullptr, + identity ? identity->identity_address_is_public : false + ); + } + }, + cb->db_entry + ); + return BLE_ERROR_NONE; + } else { + return BLE_ERROR_INVALID_STATE; + } +} + //////////////////////////////////////////////////////////////////////////// // Feature support // @@ -901,6 +948,33 @@ ble_error_t GenericSecurityManager::init_si return _pal.set_csrk(*pcsrk, local_sign_counter); } +template class TPalSecurityManager, template class SigningMonitor> +ble_error_t GenericSecurityManager::init_identity() { + if (!_db) return BLE_ERROR_INITIALIZATION_INCOMPLETE; + const irk_t *pirk = nullptr; + + irk_t irk = _db->get_local_irk(); + if (irk != irk_t()) { + pirk = &irk; + } else { + ble_error_t ret = get_random_data(irk.data(), irk.size()); + if (ret != BLE_ERROR_NONE) { + return ret; + } + + pirk = &irk; + address_t identity_address; + bool public_address; + ret = _pal.get_identity_address(identity_address, public_address); + if (ret != BLE_ERROR_NONE) { + return ret; + } + _db->set_local_identity(irk, identity_address, public_address); + } + + return _pal.set_irk(*pirk); +} + template class TPalSecurityManager, template class SigningMonitor> ble_error_t GenericSecurityManager::get_random_data(uint8_t *buffer, size_t size) { byte_array_t<8> random_data; diff --git a/features/FEATURE_BLE/targets/TARGET_CORDIO/CordioPalSecurityManager.h b/features/FEATURE_BLE/targets/TARGET_CORDIO/CordioPalSecurityManager.h index dcef01a39e..c75bc86000 100644 --- a/features/FEATURE_BLE/targets/TARGET_CORDIO/CordioPalSecurityManager.h +++ b/features/FEATURE_BLE/targets/TARGET_CORDIO/CordioPalSecurityManager.h @@ -212,6 +212,11 @@ public: */ ble_error_t set_private_address_timeout_(uint16_t timeout_in_seconds); + /** + * @see ::ble::pal::SecurityManager::get_identity_address + */ + ble_error_t get_identity_address_(address_t& address, bool& public_address); + //////////////////////////////////////////////////////////////////////////// // Keys // diff --git a/features/FEATURE_BLE/targets/TARGET_CORDIO/source/CordioPalSecurityManager.tpp b/features/FEATURE_BLE/targets/TARGET_CORDIO/source/CordioPalSecurityManager.tpp index b159561fca..479a847708 100644 --- a/features/FEATURE_BLE/targets/TARGET_CORDIO/source/CordioPalSecurityManager.tpp +++ b/features/FEATURE_BLE/targets/TARGET_CORDIO/source/CordioPalSecurityManager.tpp @@ -279,6 +279,20 @@ ble_error_t CordioSecurityManager::set_private_address_timeout_( return BLE_ERROR_NONE; } +/** + * @see ::ble::pal::SecurityManager::get_identity_address + */ +template +ble_error_t CordioSecurityManager::get_identity_address_( + address_t& address, + bool& public_address +) { + // On cordio, the public address is hardcoded as the identity address. + address = address_t(HciGetBdAddr()); + public_address = true; + return BLE_ERROR_NONE; +} + //////////////////////////////////////////////////////////////////////////// // Keys //