mediCAL's first BLE project
Fork of nRF51822 by
Diff: nordic/ble/ble_bondmngr.cpp
- Revision:
- 0:eff01767de02
- Child:
- 32:84dea0924a63
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nordic/ble/ble_bondmngr.cpp Wed Mar 26 14:38:17 2014 +0000 @@ -0,0 +1,1594 @@ +/* Copyright (c) 2012 Nordic Semiconductor. All Rights Reserved. + * + * The information contained herein is property of Nordic Semiconductor ASA. + * Terms and conditions of usage are described in detail in NORDIC + * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. + * + * Licensees are granted free, non-transferable use of the information. NO + * WARRANTY of ANY KIND is provided. This heading must NOT be removed from + * the file. + * + */ + +#include "ble_bondmngr.h" +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include "nordic_common.h" +#include "nrf_error.h" +#include "ble_gap.h" +#include "ble_srv_common.h" +#include "app_util.h" +#include "nrf_assert.h" +//#include "nrf.h" +#include "nrf51_bitfields.h" +#include "crc16.h" +#include "pstorage.h" +#include "ble_bondmngr_cfg.h" + +#define CCCD_SIZE 6 /**< Number of bytes needed for storing the state of one CCCD. */ +#define CRC_SIZE 2 /**< Size of CRC in sys_attribute data. */ +#define SYS_ATTR_BUFFER_MAX_LEN (((BLE_BONDMNGR_CCCD_COUNT + 1) * CCCD_SIZE) + CRC_SIZE) /**< Size of sys_attribute data. */ +#define MAX_NUM_CENTRAL_WHITE_LIST MIN(BLE_BONDMNGR_MAX_BONDED_CENTRALS, 8) /**< Maximum number of whitelisted centrals supported.*/ +#define MAX_BONDS_IN_FLASH 10 /**< Maximum number of bonds that can be stored in flash. */ +#define BOND_MANAGER_DATA_SIGNATURE 0x53240000 + +/**@defgroup ble_bond_mngr_sec_access Bond Manager Security Status Access Macros + * @brief The following group of macros abstract access to Security Status with a peer. + * @{ + */ + +#define SEC_STATUS_INIT_VAL 0x00 /**< Initialization value for security status flags. */ +#define ENC_STATUS_SET_VAL 0x01 /**< Bitmask for encryption status. */ +#define BOND_IN_PROGRESS_SET_VAL 0x02 /**< Bitmask for 'bonding in progress'. */ + + +/**@brief Macro for setting the Encryption Status for current link. + * + * @details Macro for setting the Encryption Status for current link. + */ +#define ENCRYPTION_STATUS_SET() \ + do \ + { \ + m_sec_con_status |= ENC_STATUS_SET_VAL; \ + } while (0) + +/**@brief Macro for getting the Encryption Status for current link. + * + * @details Macro for getting the Encryption Status for current link. + */ +#define ENCRYPTION_STATUS_GET() \ + (((m_sec_con_status & ENC_STATUS_SET_VAL) == 0) ? false : true) + +/**@brief Macro for resetting the Encryption Status for current link. + * + * @details Macro for resetting the Encryption Status for current link. + */ +#define ENCRYPTION_STATUS_RESET() \ + do \ + { \ + m_sec_con_status &= (~ENC_STATUS_SET_VAL); \ + } while (0) + + +/**@brief Macro for resetting the Bonding In Progress status for current link. + * + * @details Macro for resetting the Bonding In Progress status for current link. + */ +#define BONDING_IN_PROGRESS_STATUS_SET() \ + do \ + { \ + m_sec_con_status |= BOND_IN_PROGRESS_SET_VAL; \ + } while (0) + +/**@brief Macro for setting the Bonding In Progress status for current link. + * + * @details Macro for setting the Bonding In Progress status for current link. + */ +#define BONDING_IN_PROGRESS_STATUS_GET() \ + (((m_sec_con_status & BOND_IN_PROGRESS_SET_VAL) == 0) ? false: true) + +/**@brief Macro for resetting the Bonding In Progress status for current link. + * + * @details Macro for resetting the Bonding In Progress status for current link. + */ +#define BONDING_IN_PROGRESS_STATUS_RESET() \ + do \ + { \ + m_sec_con_status &= (~BOND_IN_PROGRESS_SET_VAL); \ + } while (0) + +/**@brief Macro for resetting all security status flags for current link. + * + * @details Macro for resetting all security status flags for current link. + */ +#define SECURITY_STATUS_RESET() \ + do \ + { \ + m_sec_con_status = SEC_STATUS_INIT_VAL; \ + } while (0) + +/** @} */ + +/**@brief Verify module's initialization status. + * + * @details Verify module's initialization status. Returns NRF_INVALID_STATE in case a module API + * is called without initializing the module. + */ +#define VERIFY_MODULE_INITIALIZED() \ + do \ + { \ + if (!m_is_bondmngr_initialized) \ + { \ + return NRF_ERROR_INVALID_STATE; \ + } \ + } while(0) + + +/**@brief This structure contains the Bonding Information for one central. + */ +typedef struct +{ + int32_t central_handle; /**< Central's handle (NOTE: Size is 32 bits just to make struct size dividable by 4). */ + ble_gap_evt_auth_status_t auth_status; /**< Central authentication data. */ + ble_gap_evt_sec_info_request_t central_id_info; /**< Central identification info. */ + ble_gap_addr_t central_addr; /**< Central's address. */ +} central_bond_t; + +STATIC_ASSERT(sizeof(central_bond_t) % 4 == 0); + +/**@brief This structure contains the System Attributes information related to one central. + */ +typedef struct +{ + int32_t central_handle; /**< Central's handle (NOTE: Size is 32 bits just to make struct size dividable by 4). */ + uint8_t sys_attr[SYS_ATTR_BUFFER_MAX_LEN]; /**< Central sys_attribute data. */ + uint32_t sys_attr_size; /**< Central sys_attribute data's size (NOTE: Size is 32 bits just to make struct size dividable by 4). */ +} central_sys_attr_t; + +STATIC_ASSERT(sizeof(central_sys_attr_t) % 4 == 0); + +/**@brief This structure contains the Bonding Information and System Attributes related to one + * central. + */ +typedef struct +{ + central_bond_t bond; /**< Bonding information. */ + central_sys_attr_t sys_attr; /**< System attribute information. */ +} central_t; + +/**@brief This structure contains the whitelisted addresses. + */ +typedef struct +{ + int8_t central_handle; /**< Central's handle. */ + ble_gap_addr_t * p_addr; /**< Pointer to the central's address if BLE_GAP_ADDR_TYPE_PUBLIC. */ +} whitelist_addr_t; + +/**@brief This structure contains the whitelisted IRKs. + */ +typedef struct +{ + int8_t central_handle; /**< Central's handle. */ + ble_gap_irk_t * p_irk; /**< Pointer to the central's irk if available. */ +} whitelist_irk_t; + +static bool m_is_bondmngr_initialized = false; /**< Flag for checking if module has been initialized. */ +static ble_bondmngr_init_t m_bondmngr_config; /**< Configuration as specified by the application. */ +static uint16_t m_conn_handle; /**< Current connection handle. */ +static central_t m_central; /**< Current central data. */ +static central_t m_centrals_db[BLE_BONDMNGR_MAX_BONDED_CENTRALS]; /**< Pointer to start of bonded centrals database. */ +static uint8_t m_centrals_in_db_count; /**< Number of bonded centrals. */ +static whitelist_addr_t m_whitelist_addr[MAX_NUM_CENTRAL_WHITE_LIST]; /**< List of central's addresses for the whitelist. */ +static whitelist_irk_t m_whitelist_irk[MAX_NUM_CENTRAL_WHITE_LIST]; /**< List of central's IRKs for the whitelist. */ +static uint8_t m_addr_count; /**< Number of addresses in the whitelist. */ +static uint8_t m_irk_count; /**< Number of IRKs in the whitelist. */ +static uint16_t m_crc_bond_info; /**< Combined CRC for all Bonding Information currently stored in flash. */ +static uint16_t m_crc_sys_attr; /**< Combined CRC for all System Attributes currently stored in flash. */ +static pstorage_handle_t mp_flash_bond_info; /**< Pointer to flash location to write next Bonding Information. */ +static pstorage_handle_t mp_flash_sys_attr; /**< Pointer to flash location to write next System Attribute information. */ +static uint8_t m_bond_info_in_flash_count; /**< Number of Bonding Information currently stored in flash. */ +static uint8_t m_sys_attr_in_flash_count; /**< Number of System Attributes currently stored in flash. */ +static uint8_t m_sec_con_status; /**< Variable to denote security status.*/ +static bool m_bond_loaded; /**< Variable to indicate if the bonding information of the currently connected central is available in the RAM.*/ +static bool m_sys_attr_loaded; /**< Variable to indicate if the system attribute information of the currently connected central is loaded from the database and set in the S110 SoftDevice.*/ +static uint32_t m_bond_crc_array[BLE_BONDMNGR_MAX_BONDED_CENTRALS]; +static uint32_t m_sys_crc_array[BLE_BONDMNGR_MAX_BONDED_CENTRALS]; + +/**@brief Function for extracting the CRC from an encoded 32 bit number that typical resides in + * the flash memory + * + * @param[in] header Header containing CRC and magic number. + * @param[out] p_crc Extracted CRC. + * + * @retval NRF_SUCCESS CRC successfully extracted. + * @retval NRF_ERROR_NOT_FOUND Flash seems to be empty. + * @retval NRF_ERROR_INVALID_DATA Header does not contain the magic number. + */ +static uint32_t crc_extract(uint32_t header, uint16_t * p_crc) +{ + if ((header & 0xFFFF0000U) == BOND_MANAGER_DATA_SIGNATURE) + { + *p_crc = (uint16_t)(header & 0x0000FFFFU); + + return NRF_SUCCESS; + } + else if (header == 0xFFFFFFFFU) + { + return NRF_ERROR_NOT_FOUND; + } + else + { + return NRF_ERROR_INVALID_DATA; + } +} + + +/**@brief Function for storing the Bonding Information of the specified central to the flash. + * + * @param[in] p_bond Bonding information to be stored. + * + * @return NRF_SUCCESS on success, an error_code otherwise. + */ +static uint32_t bond_info_store(central_bond_t * p_bond) +{ + uint32_t err_code; + pstorage_handle_t dest_block; + + // Check if flash is full + if (m_bond_info_in_flash_count >= MAX_BONDS_IN_FLASH) + { + return NRF_ERROR_NO_MEM; + } + + // Check if this is the first bond to be stored + if (m_bond_info_in_flash_count == 0) + { + // Initialize CRC + m_crc_bond_info = crc16_compute(NULL, 0, NULL); + } + + // Get block pointer from base + err_code = pstorage_block_identifier_get(&mp_flash_bond_info,m_bond_info_in_flash_count,&dest_block); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + // Write Bonding Information + err_code = pstorage_store(&dest_block, + (uint8_t *)p_bond, + sizeof(central_bond_t), + sizeof(uint32_t)); + + if (err_code != NRF_SUCCESS) + { + return err_code; + } + m_crc_bond_info = crc16_compute((uint8_t *)p_bond, + sizeof(central_bond_t), + &m_crc_bond_info); + + // Write header + m_bond_crc_array[m_bond_info_in_flash_count] = (BOND_MANAGER_DATA_SIGNATURE | m_crc_bond_info); + + err_code = pstorage_store (&dest_block, (uint8_t *)&m_bond_crc_array[m_bond_info_in_flash_count],sizeof(uint32_t),0); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + m_bond_info_in_flash_count++; + return NRF_SUCCESS; +} + + +/**@brief Function for storing the System Attributes related to a specified central in flash. + * + * @param[in] p_sys_attr System Attributes to be stored. + * + * @return NRF_SUCCESS on success, an error_code otherwise. + */ +static uint32_t sys_attr_store(central_sys_attr_t * p_sys_attr) +{ + uint32_t err_code; + pstorage_handle_t dest_block; + + // Check if flash is full. + if (m_sys_attr_in_flash_count >= MAX_BONDS_IN_FLASH) + { + return NRF_ERROR_NO_MEM; + } + + // Check if this is the first time any System Attributes is stored. + if (m_sys_attr_in_flash_count == 0) + { + // Initialize CRC + m_crc_sys_attr = crc16_compute(NULL, 0, NULL); + } + + + // Get block pointer from base + err_code = pstorage_block_identifier_get(&mp_flash_sys_attr,m_sys_attr_in_flash_count,&dest_block); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + // Write System Attributes in flash. + err_code = pstorage_store(&dest_block, + (uint8_t *)p_sys_attr, + sizeof(central_sys_attr_t), + sizeof(uint32_t)); + + if (err_code != NRF_SUCCESS) + { + return err_code; + } + m_crc_sys_attr = crc16_compute((uint8_t *)p_sys_attr, + sizeof(central_sys_attr_t), + &m_crc_sys_attr); + + // Write header. + m_sys_crc_array[m_sys_attr_in_flash_count] = (BOND_MANAGER_DATA_SIGNATURE | m_crc_sys_attr); + + err_code = pstorage_store (&dest_block, + (uint8_t *)&m_sys_crc_array[m_sys_attr_in_flash_count], + sizeof(uint32_t), + 0); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + m_sys_attr_in_flash_count++; + + + return NRF_SUCCESS; +} + + +/**@brief Function for loading the Bonding Information of one central from flash. + * + * @param[out] p_bond Loaded Bonding Information. + * + * @return NRF_SUCCESS on success, otherwise an error code. + */ +static uint32_t bonding_info_load_from_flash(central_bond_t * p_bond) +{ + pstorage_handle_t source_block; + uint32_t err_code; + uint32_t crc; + uint16_t crc_header; + + // Check if this is the first bond to be loaded, in which case the + // m_bond_info_in_flash_count variable would have the intial value 0. + if (m_bond_info_in_flash_count == 0) + { + // Initialize CRC. + m_crc_bond_info = crc16_compute(NULL, 0, NULL); + } + + // Get block pointer from base + err_code = pstorage_block_identifier_get(&mp_flash_bond_info, + m_bond_info_in_flash_count, + &source_block); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + err_code = pstorage_load((uint8_t *)&crc, &source_block, sizeof(uint32_t), 0); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + // Extract CRC from header. + err_code = crc_extract(crc, &crc_header); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + // Load central. + err_code = pstorage_load((uint8_t *)p_bond, + &source_block, + sizeof(central_bond_t), + sizeof(uint32_t)); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + // Check CRC. + m_crc_bond_info = crc16_compute((uint8_t *)p_bond, + sizeof(central_bond_t), + &m_crc_bond_info); + if (m_crc_bond_info == crc_header) + { + m_bond_info_in_flash_count++; + return NRF_SUCCESS; + } + else + { + return NRF_ERROR_INVALID_DATA; + } +} + + + +/**@brief Function for loading the System Attributes related to one central from flash. + * + * @param[out] p_sys_attr Loaded System Attributes. + * + * @return NRF_SUCCESS on success, otherwise an error code. + */ +static uint32_t sys_attr_load_from_flash(central_sys_attr_t * p_sys_attr) +{ + pstorage_handle_t source_block; + uint32_t err_code; + uint32_t crc; + uint16_t crc_header; + + // Check if this is the first time System Attributes is loaded from flash, in which case the + // m_sys_attr_in_flash_count variable would have the initial value 0. + if (m_sys_attr_in_flash_count == 0) + { + // Initialize CRC. + m_crc_sys_attr = crc16_compute(NULL, 0, NULL); + } + + // Get block pointer from base + err_code = pstorage_block_identifier_get(&mp_flash_sys_attr, + m_sys_attr_in_flash_count, + &source_block); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + err_code = pstorage_load((uint8_t *)&crc, &source_block, sizeof(uint32_t), 0); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + // Extract CRC from header. + err_code = crc_extract(crc, &crc_header); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + err_code = pstorage_load((uint8_t *)p_sys_attr, + &source_block, + sizeof(central_sys_attr_t), + sizeof(uint32_t)); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + // Check CRC. + m_crc_sys_attr = crc16_compute((uint8_t *)p_sys_attr, + sizeof(central_sys_attr_t), + &m_crc_sys_attr); + + if (m_crc_sys_attr == crc_header) + { + m_sys_attr_in_flash_count++; + return NRF_SUCCESS; + } + else + { + return NRF_ERROR_INVALID_DATA; + } +} + + +/**@brief Function for erasing the flash pages that contain Bonding Information and System + * Attributes. + * + * @return NRF_SUCCESS on success, otherwise an error code. + */ +static uint32_t flash_pages_erase(void) +{ + uint32_t err_code; + + err_code = pstorage_clear(&mp_flash_bond_info, MAX_BONDS_IN_FLASH); + + if (err_code == NRF_SUCCESS) + { + err_code = pstorage_clear(&mp_flash_sys_attr, MAX_BONDS_IN_FLASH); + } + + return err_code; +} + + +/**@brief Function for checking if Bonding Information in RAM is different from that in + * flash. + * + * @return TRUE if Bonding Information in flash and RAM are different, FALSE otherwise. + */ +static bool bond_info_changed(void) +{ + int i; + uint16_t crc = crc16_compute(NULL, 0, NULL); + + // Compute CRC for all bonds in database. + for (i = 0; i < m_centrals_in_db_count; i++) + { + crc = crc16_compute((uint8_t *)&m_centrals_db[i].bond, + sizeof(central_bond_t), + &crc); + } + + // Compare the computed CRS to CRC stored in flash. + return (crc != m_crc_bond_info); +} + + +/**@brief Function for checking if System Attributes in RAM is different from that in flash. + * + * @return TRUE if System Attributes in flash and RAM are different, FALSE otherwise. + */ +static bool sys_attr_changed(void) +{ + int i; + uint16_t crc = crc16_compute(NULL, 0, NULL); + + // Compute CRC for all System Attributes in database. + for (i = 0; i < m_centrals_in_db_count; i++) + { + crc = crc16_compute((uint8_t *)&m_centrals_db[i].sys_attr, + sizeof(central_sys_attr_t), + &crc); + } + + // Compare the CRC of System Attributes in flash with that of the System Attributes in memory. + return (crc != m_crc_sys_attr); +} + + +/**@brief Function for setting the System Attributes for specified central to the SoftDevice, or + * clearing the System Attributes if central is a previously unknown. + * + * @param[in] p_central Central for which the System Attributes is to be set. + * + * @return NRF_SUCCESS on success, otherwise an error code. + */ +static uint32_t central_sys_attr_set(central_t * p_central) +{ + uint8_t * p_sys_attr; + + if (m_central.sys_attr.sys_attr_size != 0) + { + if (m_central.sys_attr.sys_attr_size > SYS_ATTR_BUFFER_MAX_LEN) + { + return NRF_ERROR_INTERNAL; + } + + p_sys_attr = m_central.sys_attr.sys_attr; + } + else + { + p_sys_attr = NULL; + } + + return sd_ble_gatts_sys_attr_set(m_conn_handle, p_sys_attr, m_central.sys_attr.sys_attr_size); +} + + +/**@brief Function for updating the whitelist data structures. + */ +static void update_whitelist(void) +{ + int i; + + for (i = 0, m_addr_count = 0, m_irk_count = 0; i < m_centrals_in_db_count; i++) + { + central_bond_t * p_bond = &m_centrals_db[i].bond; + + if (p_bond->auth_status.central_kex.irk) + { + m_whitelist_irk[m_irk_count].central_handle = p_bond->central_handle; + m_whitelist_irk[m_irk_count].p_irk = &(p_bond->auth_status.central_keys.irk); + + m_irk_count++; + } + + if (p_bond->central_addr.addr_type != BLE_GAP_ADDR_TYPE_RANDOM_PRIVATE_RESOLVABLE) + { + m_whitelist_addr[m_addr_count].central_handle = p_bond->central_handle; + m_whitelist_addr[m_addr_count].p_addr = &(p_bond->central_addr); + + m_addr_count++; + } + } +} + + +/**@brief Function for handling the authentication status event related to a new central. + * + * @details This function adds the new central to the database and stores the central's Bonding + * Information to flash. It also notifies the application when the new bond is created, + * and sets the System Attributes to prepare the stack for connection with the new + * central. + * + * @param[in] p_auth_status New authentication status. + * + * @return NRF_SUCCESS on success, otherwise an error code. + */ +static uint32_t on_auth_status_from_new_central(ble_gap_evt_auth_status_t * p_auth_status) +{ + uint32_t err_code; + + if (m_centrals_in_db_count >= BLE_BONDMNGR_MAX_BONDED_CENTRALS) + { + return NRF_ERROR_NO_MEM; + } + + // Update central. + m_central.bond.auth_status = *p_auth_status; + m_central.bond.central_id_info.div = p_auth_status->periph_keys.enc_info.div; + m_central.sys_attr.sys_attr_size = 0; + + // Add new central to database. + m_central.bond.central_handle = m_centrals_in_db_count; + m_centrals_db[m_centrals_in_db_count++] = m_central; + + update_whitelist(); + + m_bond_loaded = true; + + // Clear System Attributes. + err_code = sd_ble_gatts_sys_attr_set(m_conn_handle, NULL, 0); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + // Write new central's Bonding Information to flash. + err_code = bond_info_store(&m_central.bond); + if ((err_code == NRF_ERROR_NO_MEM) && (m_bondmngr_config.evt_handler != NULL)) + { + ble_bondmngr_evt_t evt; + + evt.evt_type = BLE_BONDMNGR_EVT_BOND_FLASH_FULL; + evt.central_handle = m_central.bond.central_handle; + evt.central_id = m_central.bond.central_id_info.div; + + m_bondmngr_config.evt_handler(&evt); + } + else if (err_code != NRF_SUCCESS) + { + return err_code; + } + + // Pass the event to application. + if (m_bondmngr_config.evt_handler != NULL) + { + ble_bondmngr_evt_t evt; + + evt.evt_type = BLE_BONDMNGR_EVT_NEW_BOND; + evt.central_handle = m_central.bond.central_handle; + evt.central_id = m_central.bond.central_id_info.div; + + m_bondmngr_config.evt_handler(&evt); + } + + return NRF_SUCCESS; +} + + +/**@brief Function for updating the current central's entry in the database. + */ +static uint32_t central_update(void) +{ + uint32_t err_code; + int32_t central_handle = m_central.bond.central_handle; + + if ((central_handle >= 0) && (central_handle < m_centrals_in_db_count)) + { + // Update the database based on whether the bond and system attributes have + // been loaded or not to avoid overwriting information that was not used in the + // connection session. + if (m_bond_loaded) + { + m_centrals_db[central_handle].bond = m_central.bond; + } + + if (m_sys_attr_loaded) + { + m_centrals_db[central_handle].sys_attr = m_central.sys_attr; + } + + update_whitelist(); + + err_code = NRF_SUCCESS; + } + else + { + err_code = NRF_ERROR_INTERNAL; + } + + return err_code; +} + + +/**@brief Function for searching for the central in the database of known centrals. + * + * @details If the central is found, the variable m_central will be populated with all the + * information (Bonding Information and System Attributes) related to that central. + * + * @param[in] central_id Central Identifier. + * @return NRF_SUCCESS on success, otherwise an error code. + */ +static uint32_t central_find_in_db(uint16_t central_id) +{ + int i; + + for (i = 0; i < m_centrals_in_db_count; i++) + { + if (central_id == m_centrals_db[i].bond.central_id_info.div) + { + m_central = m_centrals_db[i]; + return NRF_SUCCESS; + } + } + + return NRF_ERROR_NOT_FOUND; +} + + +/**@brief Function for loading all Bonding Information and System Attributes from flash. + * + * @return NRF_SUCCESS on success, otherwise an error code. + */ +static uint32_t load_all_from_flash(void) +{ + uint32_t err_code; + int i; + + m_centrals_in_db_count = 0; + + while (m_centrals_in_db_count < BLE_BONDMNGR_MAX_BONDED_CENTRALS) + { + central_bond_t central_bond_info; + int central_handle; + + // Load Bonding Information. + err_code = bonding_info_load_from_flash(¢ral_bond_info); + if (err_code == NRF_ERROR_NOT_FOUND) + { + // No more bonds in flash. + break; + } + else if (err_code != NRF_SUCCESS) + { + return err_code; + } + + central_handle = central_bond_info.central_handle; + if (central_handle > m_centrals_in_db_count) + { + // Central handle value(s) missing in flash. This should never happen. + return NRF_ERROR_INVALID_DATA; + } + else + { + // Add/update Bonding Information in central array. + m_centrals_db[central_handle].bond = central_bond_info; + if (central_handle == m_centrals_in_db_count) + { + // New central handle, clear System Attributes. + m_centrals_db[central_handle].sys_attr.sys_attr_size = 0; + m_centrals_db[central_handle].sys_attr.central_handle = INVALID_CENTRAL_HANDLE; + m_centrals_in_db_count++; + } + else + { + // Entry was updated, do nothing. + } + } + } + + // Load System Attributes for all previously known centrals. + for (i = 0; i < m_centrals_in_db_count; i++) + { + central_sys_attr_t central_sys_attr; + + // Load System Attributes. + err_code = sys_attr_load_from_flash(¢ral_sys_attr); + if (err_code == NRF_ERROR_NOT_FOUND) + { + // No more System Attributes in flash. + break; + } + else if (err_code != NRF_SUCCESS) + { + return err_code; + } + + if (central_sys_attr.central_handle > m_centrals_in_db_count) + { + // Central handle value(s) missing in flash. This should never happen. + return NRF_ERROR_INVALID_DATA; + } + else + { + // Add/update Bonding Information in central array. + m_centrals_db[central_sys_attr.central_handle].sys_attr = central_sys_attr; + } + } + + // Initialize the remaining empty bond entries in the memory. + for (i = m_centrals_in_db_count; i < BLE_BONDMNGR_MAX_BONDED_CENTRALS; i++) + { + m_centrals_db[i].bond.central_handle = INVALID_CENTRAL_HANDLE; + m_centrals_db[i].sys_attr.sys_attr_size = 0; + m_centrals_db[i].sys_attr.central_handle = INVALID_CENTRAL_HANDLE; + } + + // Update whitelist data structures. + update_whitelist(); + + return NRF_SUCCESS; +} + + +/**@brief Function for handling the connected event received from the BLE stack. + * + * @param[in] p_ble_evt Event received from the BLE stack. + */ +static void on_connect(ble_evt_t * p_ble_evt) +{ + m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle; + + m_central.bond.central_handle = INVALID_CENTRAL_HANDLE; + m_central.bond.central_addr = p_ble_evt->evt.gap_evt.params.connected.peer_addr; + m_central.sys_attr.sys_attr_size = 0; + + if (p_ble_evt->evt.gap_evt.params.connected.irk_match) + { + uint8_t irk_idx = p_ble_evt->evt.gap_evt.params.connected.irk_match_idx; + + if ((irk_idx >= MAX_NUM_CENTRAL_WHITE_LIST) || + (m_whitelist_irk[irk_idx].central_handle >= BLE_BONDMNGR_MAX_BONDED_CENTRALS)) + { + m_bondmngr_config.error_handler(NRF_ERROR_INTERNAL); + } + else + { + m_central = m_centrals_db[m_whitelist_irk[irk_idx].central_handle]; + } + } + else + { + int i; + + for (i = 0; i < m_addr_count; i++) + { + ble_gap_addr_t * p_cur_addr = m_whitelist_addr[i].p_addr; + + if (memcmp(p_cur_addr->addr, m_central.bond.central_addr.addr, BLE_GAP_ADDR_LEN) == 0) + { + m_central = m_centrals_db[m_whitelist_addr[i].central_handle]; + break; + } + } + } + + if (m_central.bond.central_handle != INVALID_CENTRAL_HANDLE) + { + // Reset bond and system attributes loaded variables. + m_bond_loaded = false; + m_sys_attr_loaded = false; + + // Do not set the system attributes of the central on connection. + if (m_bondmngr_config.evt_handler != NULL) + { + ble_bondmngr_evt_t evt; + + evt.evt_type = BLE_BONDMNGR_EVT_CONN_TO_BONDED_CENTRAL; + evt.central_handle = m_central.bond.central_handle; + evt.central_id = m_central.bond.central_id_info.div; + + m_bondmngr_config.evt_handler(&evt); + } + } +} + + +/**@brief Function for handling the 'System Attributes Missing' event received from the + * SoftDevice. + * + * @param[in] p_ble_evt Event received from the BLE stack. + */ +static void on_sys_attr_missing(ble_evt_t * p_ble_evt) +{ + uint32_t err_code; + + if ( + (m_central.bond.central_handle == INVALID_CENTRAL_HANDLE) || + !ENCRYPTION_STATUS_GET() || + BONDING_IN_PROGRESS_STATUS_GET() + ) + { + err_code = sd_ble_gatts_sys_attr_set(m_conn_handle, NULL, 0); + } + else + { + // Current central is valid, use its data. Set the corresponding sys_attr. + err_code = central_sys_attr_set(&m_central); + if (err_code == NRF_SUCCESS) + { + // Set System Attributes loaded status variable. + m_sys_attr_loaded = true; + } + } + + if (err_code != NRF_SUCCESS) + { + m_bondmngr_config.error_handler(err_code); + } +} + + +/**@brief Function for handling the new authentication status event, received from the + * SoftDevice, related to an already bonded central. + * + * @details This function also writes the updated Bonding Information to flash and notifies the + * application. + * + * @param[in] p_auth_status Updated authentication status. + */ +static void auth_status_update(ble_gap_evt_auth_status_t * p_auth_status) +{ + uint32_t err_code; + + // Authentication status changed, update Bonding Information. + m_central.bond.auth_status = *p_auth_status; + m_central.bond.central_id_info.div = p_auth_status->periph_keys.enc_info.div; + + memset(&(m_centrals_db[m_central.bond.central_handle]), 0, sizeof(central_t)); + m_centrals_db[m_central.bond.central_handle] = m_central; + + // Write updated Bonding Information to flash. + err_code = bond_info_store(&m_central.bond); + if ((err_code == NRF_ERROR_NO_MEM) && (m_bondmngr_config.evt_handler != NULL)) + { + ble_bondmngr_evt_t evt; + + evt.evt_type = BLE_BONDMNGR_EVT_BOND_FLASH_FULL; + evt.central_handle = m_central.bond.central_handle; + evt.central_id = m_central.bond.central_id_info.div; + + m_bondmngr_config.evt_handler(&evt); + } + else if (err_code != NRF_SUCCESS) + { + m_bondmngr_config.error_handler(err_code); + } + + // Pass the event to the application. + if (m_bondmngr_config.evt_handler != NULL) + { + ble_bondmngr_evt_t evt; + + evt.evt_type = BLE_BONDMNGR_EVT_AUTH_STATUS_UPDATED; + evt.central_handle = m_central.bond.central_handle; + evt.central_id = m_central.bond.central_id_info.div; + + m_bondmngr_config.evt_handler(&evt); + } +} + + +/**@brief Function for handling the Authentication Status event received from the BLE stack. + * + * @param[in] p_ble_evt Event received from the BLE stack. + */ +static void on_auth_status(ble_gap_evt_auth_status_t * p_auth_status) +{ + if (p_auth_status->auth_status != BLE_GAP_SEC_STATUS_SUCCESS) + { + return; + } + + // Verify if its pairing and not bonding + if (!ENCRYPTION_STATUS_GET()) + { + return; + } + + if (m_central.bond.central_handle == INVALID_CENTRAL_HANDLE) + { + uint32_t err_code = central_find_in_db(p_auth_status->periph_keys.enc_info.div); + + if (err_code == NRF_SUCCESS) + { + // Possible DIV Collision indicate error to application, + // not storing the new LTK + err_code = NRF_ERROR_FORBIDDEN; + } + else + { + // Add the new device to data base + err_code = on_auth_status_from_new_central(p_auth_status); + } + + if (err_code != NRF_SUCCESS) + { + m_bondmngr_config.error_handler(err_code); + } + } + else + { + m_bond_loaded = true; + + // Receiving a auth status again when already in have existing information! + auth_status_update(p_auth_status); + } +} + + +/**@brief Function for handling the Security Info Request event received from the BLE stack. + * + * @param[in] p_ble_evt Event received from the BLE stack. + */ +static void on_sec_info_request(ble_evt_t * p_ble_evt) +{ + uint32_t err_code; + + err_code = central_find_in_db(p_ble_evt->evt.gap_evt.params.sec_info_request.div); + if (err_code == NRF_SUCCESS) + { + // Bond information has been found and loaded for security procedures. Reflect this in the + // status variable + m_bond_loaded = true; + + // Central found in the list of bonded central. Use the encryption info for this central. + err_code = sd_ble_gap_sec_info_reply(m_conn_handle, + &m_central.bond.auth_status.periph_keys.enc_info, + NULL); + if (err_code != NRF_SUCCESS) + { + m_bondmngr_config.error_handler(err_code); + } + + // Do not set the sys_attr yet, should be set only when sec_update is successful. + } + else if (err_code == NRF_ERROR_NOT_FOUND) + { + m_central.bond.central_id_info = p_ble_evt->evt.gap_evt.params.sec_info_request; + + // New central. + err_code = sd_ble_gap_sec_info_reply(m_conn_handle, NULL, NULL); + if (err_code != NRF_SUCCESS) + { + m_bondmngr_config.error_handler(err_code); + } + + // Initialize the sys_attr. + err_code = sd_ble_gatts_sys_attr_set(m_conn_handle, NULL, 0); + } + + if (err_code != NRF_SUCCESS) + { + m_bondmngr_config.error_handler(err_code); + } +} + + +/**@brief Function for handling the Connection Security Update event received from the BLE + * stack. + * + * @param[in] p_ble_evt Event received from the BLE stack. + */ +static void on_sec_update(ble_gap_evt_conn_sec_update_t * p_sec_update) +{ + uint8_t security_mode = p_sec_update->conn_sec.sec_mode.sm; + uint8_t security_level = p_sec_update->conn_sec.sec_mode.lv; + + if (((security_mode == 1) && (security_level > 1)) || + ((security_mode == 2) && (security_level != 0))) + { + ENCRYPTION_STATUS_SET(); + + uint32_t err_code = central_sys_attr_set(&m_central); + + if (err_code != NRF_SUCCESS) + { + m_bondmngr_config.error_handler(err_code); + } + else + { + m_sys_attr_loaded = true; + } + + if (m_bondmngr_config.evt_handler != NULL) + { + ble_bondmngr_evt_t evt; + + evt.evt_type = BLE_BONDMNGR_EVT_ENCRYPTED; + evt.central_handle = m_central.bond.central_handle; + evt.central_id = m_central.bond.central_id_info.div; + + m_bondmngr_config.evt_handler(&evt); + } + } +} + + +/**@brief Function for handling the Connection Security Parameters Request event received from + * the BLE stack. + * + * @param[in] p_ble_evt Event received from the BLE stack. + */ +static void on_sec_param_request(ble_gap_evt_sec_params_request_t * p_sec_update) +{ + if (p_sec_update->peer_params.bond) + { + BONDING_IN_PROGRESS_STATUS_SET(); + + if (m_central.bond.central_handle != INVALID_CENTRAL_HANDLE) + { + // Bonding request received from a bonded central, make all system attributes null + m_central.sys_attr.sys_attr_size = 0; + memset(m_central.sys_attr.sys_attr, 0, SYS_ATTR_BUFFER_MAX_LEN); + } + } +} + + +void ble_bondmngr_on_ble_evt(ble_evt_t * p_ble_evt) +{ + if (!m_is_bondmngr_initialized) + { + m_bondmngr_config.error_handler(NRF_ERROR_INVALID_STATE); + } + + switch (p_ble_evt->header.evt_id) + { + case BLE_GAP_EVT_CONNECTED: + on_connect(p_ble_evt); + break; + + // NOTE: All actions to be taken on the Disconnected event are performed in + // ble_bondmngr_bonded_centrals_store(). This function must be called from the + // Disconnected handler of the application before advertising is restarted (to make + // sure the flash blocks are cleared while the radio is inactive). + case BLE_GAP_EVT_DISCONNECTED: + SECURITY_STATUS_RESET(); + break; + + case BLE_GATTS_EVT_SYS_ATTR_MISSING: + on_sys_attr_missing(p_ble_evt); + break; + + case BLE_GAP_EVT_AUTH_STATUS: + on_auth_status(&p_ble_evt->evt.gap_evt.params.auth_status); + break; + + case BLE_GAP_EVT_SEC_INFO_REQUEST: + on_sec_info_request(p_ble_evt); + break; + + case BLE_GAP_EVT_SEC_PARAMS_REQUEST: + on_sec_param_request(&p_ble_evt->evt.gap_evt.params.sec_params_request); + break; + + case BLE_GAP_EVT_CONN_SEC_UPDATE: + on_sec_update(&p_ble_evt->evt.gap_evt.params.conn_sec_update); + break; + + default: + // No implementation needed. + break; + } +} + + +uint32_t ble_bondmngr_bonded_centrals_store(void) +{ + uint32_t err_code; + int i; + + VERIFY_MODULE_INITIALIZED(); + + if (m_central.bond.central_handle != INVALID_CENTRAL_HANDLE) + { + // Fetch System Attributes from stack. + uint16_t sys_attr_size = SYS_ATTR_BUFFER_MAX_LEN; + + err_code = sd_ble_gatts_sys_attr_get(m_conn_handle, + m_central.sys_attr.sys_attr, + &sys_attr_size); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + m_central.sys_attr.central_handle = m_central.bond.central_handle; + m_central.sys_attr.sys_attr_size = (uint16_t)sys_attr_size; + + // Update the current central. + err_code = central_update(); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + } + + // Save Bonding Information if changed. + if (bond_info_changed()) + { + // Erase flash page. + err_code = pstorage_clear(&mp_flash_bond_info,MAX_BONDS_IN_FLASH); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + // Store bond information for all centrals. + m_bond_info_in_flash_count = 0; + for (i = 0; i < m_centrals_in_db_count; i++) + { + err_code = bond_info_store(&m_centrals_db[i].bond); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + } + } + + // Save System Attributes, if changed. + if (sys_attr_changed()) + { + // Erase flash page. + err_code = pstorage_clear(&mp_flash_sys_attr, MAX_BONDS_IN_FLASH); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + // Store System Attributes for all centrals. + m_sys_attr_in_flash_count = 0; + for (i = 0; i < m_centrals_in_db_count; i++) + { + err_code = sys_attr_store(&m_centrals_db[i].sys_attr); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + } + } + + m_conn_handle = BLE_CONN_HANDLE_INVALID; + m_central.bond.central_handle = INVALID_CENTRAL_HANDLE; + m_central.sys_attr.central_handle = INVALID_CENTRAL_HANDLE; + m_central.sys_attr.sys_attr_size = 0; + m_bond_loaded = false; + m_sys_attr_loaded = false; + + return NRF_SUCCESS; +} + + +uint32_t ble_bondmngr_sys_attr_store(void) +{ + uint32_t err_code; + + if (m_central.sys_attr.sys_attr_size == 0) + { + // Connected to new central. So the flash block for System Attributes for this + // central is empty. Hence no erase is needed. + + uint16_t sys_attr_size = SYS_ATTR_BUFFER_MAX_LEN; + + // Fetch System Attributes from stack. + err_code = sd_ble_gatts_sys_attr_get(m_conn_handle, + m_central.sys_attr.sys_attr, + &sys_attr_size); + + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + m_central.sys_attr.central_handle = m_central.bond.central_handle; + m_central.sys_attr.sys_attr_size = (uint16_t)sys_attr_size; + + // Copy the System Attributes to database. + m_centrals_db[m_central.bond.central_handle].sys_attr = m_central.sys_attr; + + // Write new central's System Attributes to flash. + return (sys_attr_store(&m_central.sys_attr)); + } + else + { + // Will not write to flash because System Attributes of an old central would already be + // in flash and so this operation needs a flash erase operation. + return NRF_ERROR_INVALID_STATE; + } +} + + +uint32_t ble_bondmngr_bonded_centrals_delete(void) +{ + VERIFY_MODULE_INITIALIZED(); + + m_centrals_in_db_count = 0; + m_bond_info_in_flash_count = 0; + m_sys_attr_in_flash_count = 0; + + return flash_pages_erase(); +} + + +uint32_t ble_bondmngr_central_addr_get(int8_t central_handle, ble_gap_addr_t * p_central_addr) +{ + if ( + (central_handle == INVALID_CENTRAL_HANDLE) || + (central_handle >= m_centrals_in_db_count) || + (p_central_addr == NULL) || + ( + m_centrals_db[central_handle].bond.central_addr.addr_type + == + BLE_GAP_ADDR_TYPE_RANDOM_PRIVATE_RESOLVABLE + ) + ) + { + return NRF_ERROR_INVALID_PARAM; + } + + *p_central_addr = m_centrals_db[central_handle].bond.central_addr; + return NRF_SUCCESS; +} + + +uint32_t ble_bondmngr_whitelist_get(ble_gap_whitelist_t * p_whitelist) +{ + static ble_gap_addr_t * s_addr[MAX_NUM_CENTRAL_WHITE_LIST]; + static ble_gap_irk_t * s_irk[MAX_NUM_CENTRAL_WHITE_LIST]; + + int i; + + for (i = 0; i < m_irk_count; i++) + { + s_irk[i] = m_whitelist_irk[i].p_irk; + } + for (i = 0; i < m_addr_count; i++) + { + s_addr[i] = m_whitelist_addr[i].p_addr; + } + + p_whitelist->addr_count = m_addr_count; + p_whitelist->pp_addrs = (m_addr_count != 0) ? s_addr : NULL; + p_whitelist->irk_count = m_irk_count; + p_whitelist->pp_irks = (m_irk_count != 0) ? s_irk : NULL; + + return NRF_SUCCESS; +} + + +static void bm_pstorage_cb_handler(pstorage_handle_t * handle, + uint8_t op_code, + uint32_t result, + uint8_t * p_data, + uint32_t data_len) +{ + if (result != NRF_SUCCESS) + { + m_bondmngr_config.error_handler(result); + } +} + + +uint32_t ble_bondmngr_init(ble_bondmngr_init_t * p_init) +{ + pstorage_module_param_t param; + uint32_t err_code; + + if (p_init->error_handler == NULL) + { + return NRF_ERROR_INVALID_PARAM; + } + + if (BLE_BONDMNGR_MAX_BONDED_CENTRALS > MAX_BONDS_IN_FLASH) + { + return NRF_ERROR_DATA_SIZE; + } + + param.block_size = sizeof (central_bond_t) + sizeof (uint32_t); + param.block_count = MAX_BONDS_IN_FLASH; + param.cb = bm_pstorage_cb_handler; + + // Blocks are requested twice, once for bond information and once for system attributes. + // The number of blocks requested has to be the maximum number of bonded devices that + // need to be supported. However, the size of blocks can be different if the sizes of + // system attributes and bonds are not too close. + err_code = pstorage_register(¶m, &mp_flash_bond_info); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + param.block_size = sizeof(central_sys_attr_t) + sizeof(uint32_t); + + err_code = pstorage_register(¶m, &mp_flash_sys_attr); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + m_bondmngr_config = *p_init; + + memset(&m_central, 0, sizeof(central_t)); + + m_central.bond.central_handle = INVALID_CENTRAL_HANDLE; + m_conn_handle = BLE_CONN_HANDLE_INVALID; + m_centrals_in_db_count = 0; + m_bond_info_in_flash_count = 0; + m_sys_attr_in_flash_count = 0; + + SECURITY_STATUS_RESET(); + + // Erase all stored centrals if specified. + if (m_bondmngr_config.bonds_delete) + { + err_code = flash_pages_erase(); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + m_centrals_in_db_count = 0; + int i; + for (i = m_centrals_in_db_count; i < BLE_BONDMNGR_MAX_BONDED_CENTRALS; i++) + { + m_centrals_db[i].bond.central_handle = INVALID_CENTRAL_HANDLE; + m_centrals_db[i].sys_attr.sys_attr_size = 0; + m_centrals_db[i].sys_attr.central_handle = INVALID_CENTRAL_HANDLE; + } + } + else + { + // Load bond manager data from flash. + err_code = load_all_from_flash(); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + } + + m_is_bondmngr_initialized = true; + + return NRF_SUCCESS; +} + + +uint32_t ble_bondmngr_central_ids_get(uint16_t * p_central_ids, uint16_t * p_length) +{ + VERIFY_MODULE_INITIALIZED(); + int i; + if (p_length == NULL) + { + return NRF_ERROR_NULL; + } + + if (*p_length < m_centrals_in_db_count) + { + // Length of the input array is not enough to fit all known central identifiers. + return NRF_ERROR_DATA_SIZE; + } + + *p_length = m_centrals_in_db_count; + if (p_central_ids == NULL) + { + // Only the length field was required to be filled. + return NRF_SUCCESS; + } + + for (i = 0; i < m_centrals_in_db_count; i++) + { + p_central_ids[i] = m_centrals_db[i].bond.central_id_info.div; + } + + return NRF_SUCCESS; +} + + +uint32_t ble_bondmngr_bonded_central_delete(uint16_t central_id) +{ + VERIFY_MODULE_INITIALIZED(); + + int8_t central_handle_to_be_deleted = INVALID_CENTRAL_HANDLE; + uint8_t i; + + // Search for the handle of the central. + for (i = 0; i < m_centrals_in_db_count; i++) + { + if (m_centrals_db[i].bond.central_id_info.div == central_id) + { + central_handle_to_be_deleted = i; + break; + } + } + + if (central_handle_to_be_deleted == INVALID_CENTRAL_HANDLE) + { + // Central ID not found. + return NRF_ERROR_NOT_FOUND; + } + + // Delete the central in RAM. + for (i = central_handle_to_be_deleted; i < (m_centrals_in_db_count - 1); i++) + { + // Overwrite the current central entry with the next one. + m_centrals_db[i] = m_centrals_db[i + 1]; + + // Decrement the value of handle. + m_centrals_db[i].bond.central_handle--; + if (INVALID_CENTRAL_HANDLE != m_centrals_db[i].sys_attr.central_handle) + { + m_centrals_db[i].sys_attr.central_handle--; + } + } + + // Clear the last database entry. + memset(&(m_centrals_db[m_centrals_in_db_count - 1]), 0, sizeof(central_t)); + + m_centrals_in_db_count--; + + uint32_t err_code; + + // Reinitialize the pointers to the memory where bonding info and System Attributes are stored + // in flash. + // Refresh the data in the flash memory (both Bonding Information and System Attributes). + // Erase and rewrite bonding info and System Attributes. + + err_code = flash_pages_erase(); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + m_bond_info_in_flash_count = 0; + m_sys_attr_in_flash_count = 0; + + for (i = 0; i < m_centrals_in_db_count; i++) + { + err_code = bond_info_store(&(m_centrals_db[i].bond)); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + } + + for (i = 0; i < m_centrals_in_db_count; i++) + { + err_code = sys_attr_store(&(m_centrals_db[i].sys_attr)); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + } + + update_whitelist(); + + return NRF_SUCCESS; +} + + +uint32_t ble_bondmngr_is_link_encrypted (bool * status) +{ + VERIFY_MODULE_INITIALIZED(); + + (*status) = ENCRYPTION_STATUS_GET(); + + return NRF_SUCCESS; +}