From c9b6bb9bbbf2755811267bd44598b7deacc4c9de Mon Sep 17 00:00:00 2001 From: Andres Amaya Garcia Date: Fri, 8 Jan 2016 10:46:11 +0000 Subject: [PATCH] Almost complete implementation of whitelisting API --- source/btle/btle_security.cpp | 15 ++- source/btle/btle_security.h | 6 +- source/nRF5xGap.cpp | 187 ++++++++++++++++++++++++++++++---- source/nRF5xGap.h | 87 +++++++--------- source/nRF5xSecurityManager.h | 14 +++ 5 files changed, 233 insertions(+), 76 deletions(-) diff --git a/source/btle/btle_security.cpp b/source/btle/btle_security.cpp index 6f21b19..74f8a2a 100644 --- a/source/btle/btle_security.cpp +++ b/source/btle/btle_security.cpp @@ -21,11 +21,13 @@ extern "C" { #include "pstorage.h" #include "device_manager.h" +#include "id_manager.h" } #include "btle_security.h" static dm_application_instance_t applicationInstance; +static bool initialized = false; static ret_code_t dm_handler(dm_handle_t const *p_handle, dm_event_t const *p_event, ret_code_t event_result); // default security parameters @@ -43,7 +45,7 @@ static ble_gap_sec_params_t securityParameters = { }, /**< Key distribution bitmap: keys that the peripheral device will distribute. */ }; -ble_error_t btle_createWhitelistFromBonds(ble_gap_whitelist_t *p_whitelist) +ble_error_t btle_createWhitelistFromBondTable(ble_gap_whitelist_t *p_whitelist) { ret_code_t err = dm_whitelist_create(&applicationInstance, p_whitelist); if (err == NRF_SUCCESS) { @@ -55,6 +57,11 @@ ble_error_t btle_createWhitelistFromBonds(ble_gap_whitelist_t *p_whitelist) } } +bool btle_hasInitializedSecurity(void) +{ + return initialized; +} + ble_error_t btle_initializeSecurity(bool enableBonding, bool requireMITM, @@ -62,7 +69,6 @@ btle_initializeSecurity(bool enableBonding, const SecurityManager::Passkey_t passkey) { /* guard against multiple initializations */ - static bool initialized = false; if (initialized) { return BLE_ERROR_NONE; } @@ -274,3 +280,8 @@ dm_handler(dm_handle_t const *p_handle, dm_event_t const *p_event, ret_code_t ev return NRF_SUCCESS; } + +bool btle_matchAddressAndIrk(ble_gap_addr_t const * p_addr, ble_gap_irk_t const * p_irk) +{ + return im_address_resolve(p_addr, p_irk); +} diff --git a/source/btle/btle_security.h b/source/btle/btle_security.h index 44d6233..3db36ee 100644 --- a/source/btle/btle_security.h +++ b/source/btle/btle_security.h @@ -40,7 +40,7 @@ ble_error_t btle_initializeSecurity(bool en SecurityManager::SecurityIOCapabilities_t iocaps = SecurityManager::IO_CAPS_NONE, const SecurityManager::Passkey_t passkey = NULL); -ble_error_t btle_createWhitelistFromBonds(ble_gap_whitelist_t *p_whitelist); +ble_error_t btle_createWhitelistFromBondTable(ble_gap_whitelist_t *p_whitelist); /** * Get the security status of a link. @@ -77,4 +77,8 @@ ble_error_t btle_setLinkSecurity(Gap::Handle_t connectionHandle, SecurityManager */ ble_error_t btle_purgeAllBondingState(void); +bool btle_hasInitializedSecurity(void); + +bool btle_matchAddressAndIrk(ble_gap_addr_t const * p_addr, ble_gap_irk_t const * p_irk); + #endif /* _BTLE_SECURITY_H_ */ diff --git a/source/nRF5xGap.cpp b/source/nRF5xGap.cpp index 67338c5..99f7d14 100644 --- a/source/nRF5xGap.cpp +++ b/source/nRF5xGap.cpp @@ -168,6 +168,18 @@ ble_error_t nRF5xGap::startAdvertising(const GapAdvertisingParams ¶ms) return BLE_ERROR_PARAM_OUT_OF_RANGE; } + /* Add missing IRKs to whitelist from the bond table held by the SoftDevice */ + if (advertisingPolicyMode != Gap::ADV_POLICY_IGNORE_WHITELIST) { + ble_error_t error = generateStackWhitelist(); + if (error != BLE_ERROR_NONE) { + return error; + } + } else { + /* Reset the whitelist table to avoid any errors */ + whitelist.addr_count = 0; + whitelist.irk_count = 0; + } + /* Start Advertising */ ble_gap_adv_params_t adv_para = {0}; @@ -185,6 +197,47 @@ ble_error_t nRF5xGap::startAdvertising(const GapAdvertisingParams ¶ms) return BLE_ERROR_NONE; } +/* Observer role is not supported by S110, return BLE_ERROR_NOT_IMPLEMENTED */ +#if !defined(TARGET_MCU_NRF51_16K_S110) && !defined(TARGET_MCU_NRF51_32K_S110) +ble_error_t nRF5xGap::startRadioScan(const GapScanningParams &scanningParams) { + + /* Add missing IRKs to whitelist from the bond table held by the SoftDevice */ + if (scanningPolicyMode != Gap::SCAN_POLICY_IGNORE_WHITELIST) { + ble_error_t error = generateStackWhitelist(); + if (error != BLE_ERROR_NONE) { + return error; + } + } else { + /* Reset the whitelist table to avoid any errors */ + whitelist.addr_count = 0; + whitelist.irk_count = 0; + } + + ble_gap_scan_params_t scanParams = { + .active = scanningParams.getActiveScanning(), /**< If 1, perform active scanning (scan requests). */ + .selective = scanningPolicyMode, /**< If 1, ignore unknown devices (non whitelisted). */ + .p_whitelist = &whitelist, /**< Pointer to whitelist, NULL if none is given. */ + .interval = scanningParams.getInterval(), /**< Scan interval between 0x0004 and 0x4000 in 0.625ms units (2.5ms to 10.24s). */ + .window = scanningParams.getWindow(), /**< Scan window between 0x0004 and 0x4000 in 0.625ms units (2.5ms to 10.24s). */ + .timeout = scanningParams.getTimeout(), /**< Scan timeout between 0x0001 and 0xFFFF in seconds, 0x0000 disables timeout. */ + }; + + if (sd_ble_gap_scan_start(&scanParams) != NRF_SUCCESS) { + return BLE_ERROR_PARAM_OUT_OF_RANGE; + } + + return BLE_ERROR_NONE; +} + +ble_error_t nRF5xGap::stopScan(void) { + if (sd_ble_gap_scan_stop() == NRF_SUCCESS) { + return BLE_ERROR_NONE; + } + + return BLE_STACK_BUSY; +} +#endif + /**************************************************************************/ /*! @brief Stops the BLE HW and disconnects from any devices @@ -233,6 +286,18 @@ ble_error_t nRF5xGap::connect(const Address_t peerAddr, connParams.conn_sup_timeout = 600; } + /* Add missing IRKs to whitelist from the bond table held by the SoftDevice */ + if (scanningPolicyMode != Gap::SCAN_POLICY_IGNORE_WHITELIST) { + ble_error_t error = generateStackWhitelist(); + if (error != BLE_ERROR_NONE) { + return error; + } + } else { + /* Reset the whitelist table to avoid any errors */ + whitelist.addr_count = 0; + whitelist.irk_count = 0; + } + ble_gap_scan_params_t scanParams; scanParams.selective = scanningPolicyMode; /**< If 1, ignore unknown devices (non whitelisted). */ scanParams.p_whitelist = &whitelist; /**< Pointer to whitelist, NULL if none is given. */ @@ -508,50 +573,58 @@ void nRF5xGap::getPermittedTxPowerValues(const int8_t **valueArrayPP, size_t *co *countP = sizeof(permittedTxValues) / sizeof(int8_t); } -/////////////////////////WHITELISTING -int8_t nRF5xGap::getMaxWhitelistSize(void) const +uint8_t nRF5xGap::getMaxWhitelistSize(void) const { return YOTTA_CFG_WHITELIST_MAX_SIZE; } -ble_error_t nRF5xGap::getWhitelist(std::set &whitelist) const +ble_error_t nRF5xGap::getWhitelist(Gap::Whitelist_t &whitelistOut) const { - for (uint8_t i = 0; i < whitelistAddressesSize; i++) { - BLEProtocol::Address_t addr;//((BLEProtocol::AddressType_t)whitelistAddrs[i].addr_type, (BLEProtocol::AddressBytes_t) whitelistAddrs[i].addr); - addr.type = (BLEProtocol::AddressType_t) whitelistAddrs[i].addr_type; - memcpy(addr.address, whitelistAddrs[i].addr, sizeof(BLEProtocol::AddressBytes_t)); - whitelist.insert(addr); + uint8_t i; + for (i = 0; i < whitelistAddressesSize && i < whitelistOut.capacity; ++i) { + memcpy(&whitelistOut.addresses[i], &whitelistAddresses[i], sizeof(BLEProtocol::Address_t)); } + whitelistOut.size = i; + return BLE_ERROR_NONE; } -ble_error_t nRF5xGap::setWhitelist(std::set whitelistIn) +ble_error_t nRF5xGap::setWhitelist(const Gap::Whitelist_t &whitelistIn) { + if (whitelistIn.size > getMaxWhitelistSize()) { + return BLE_ERROR_PARAM_OUT_OF_RANGE; + } + whitelistAddressesSize = 0; - for (std::set::iterator it = whitelistIn.begin(); it != whitelistIn.end(); it++) { - whitelistAddrs[whitelistAddressesSize].addr_type = it->type; - memcpy(whitelistAddrs[whitelistAddressesSize].addr, it->address, sizeof(BLEProtocol::AddressBytes_t)); + for (uint8_t i = 0; i < whitelistIn.size; ++i) { + if (whitelistIn.addresses[i].type == BLEProtocol::AddressType_t::RANDOM_PRIVATE_NON_RESOLVABLE) { + /* This is not allowed because it is completely meaningless */ + return BLE_ERROR_INVALID_PARAM; + } + memcpy(&whitelistAddresses[whitelistAddressesSize], &whitelistIn.addresses[i], sizeof(BLEProtocol::Address_t)); whitelistAddressesSize++; } - whitelist.addr_count = whitelistAddressesSize; - whitelist.irk_count = whitelistIrksSize; + return BLE_ERROR_NONE; } -// Accessors -void nRF5xGap::setAdvertisingPolicyMode(Gap::AdvertisingPolicyMode_t mode) +ble_error_t nRF5xGap::setAdvertisingPolicyMode(Gap::AdvertisingPolicyMode_t mode) { advertisingPolicyMode = mode; + + return BLE_ERROR_NONE; } -void nRF5xGap::setScanningPolicyMode(Gap::ScanningPolicyMode_t mode) +ble_error_t nRF5xGap::setScanningPolicyMode(Gap::ScanningPolicyMode_t mode) { scanningPolicyMode = mode; + + return BLE_ERROR_NONE; } -void nRF5xGap::setInitiatorPolicyMode(Gap::InitiatorPolicyMode_t mode) +ble_error_t nRF5xGap::setInitiatorPolicyMode(Gap::InitiatorPolicyMode_t mode) { - initiatorPolicyMode = mode; + return BLE_ERROR_NOT_IMPLEMENTED; } Gap::AdvertisingPolicyMode_t nRF5xGap::getAdvertisingPolicyMode(void) const @@ -566,6 +639,78 @@ Gap::ScanningPolicyMode_t nRF5xGap::getScanningPolicyMode(void) const Gap::InitiatorPolicyMode_t nRF5xGap::getInitiatorPolicyMode(void) const { - return initiatorPolicyMode; + return Gap::INIT_POLICY_IGNORE_WHITELIST; +} + +ble_error_t nRF5xGap::generateStackWhitelist(void) +{ + ble_gap_whitelist_t whitelistFromBondTable; + ble_gap_addr_t *addressPtr[1]; + ble_gap_irk_t *irkPtr[YOTTA_CFG_IRK_TABLE_MAX_SIZE]; + + nRF5xSecurityManager& securityManager = (nRF5xSecurityManager&) nRF5xn::Instance(0).getSecurityManager(); + + if (securityManager.hasInitialized()) { + /* We do not care about the addresses, set the count to 0 */ + whitelistFromBondTable.addr_count = 0; + /* The Nordic SDK will return a failure if we set pp_addr to NULL */ + whitelistFromBondTable.pp_addrs = addressPtr; + /* We want all the IRKs we can get because we do not know which ones match the addresses */ + whitelistFromBondTable.irk_count = YOTTA_CFG_IRK_TABLE_MAX_SIZE; + whitelistFromBondTable.pp_irks = irkPtr; + + /* Use the security manager to get the IRKs from the bond table */ + ble_error_t error = securityManager.createWhitelistFromBondTable(whitelistFromBondTable); + if (error != BLE_ERROR_NONE) { + return error; + } + } else { + /** + * If there is no security manager then we cannot access the bond table, + * so disable IRK matching + */ + whitelistFromBondTable.addr_count = 0; + whitelistFromBondTable.irk_count = 0; + } + + /** + * For every private resolvable address in the local whitelist check if + * there is an IRK for said address in the bond table and add it to the + * local IRK list. + */ + whitelist.irk_count = 0; + whitelist.addr_count = 0; + for (uint8_t i = 0; i < whitelistAddressesSize; ++i) { + if (whitelistAddresses[i].addr_type == BLEProtocol::AddressType_t::RANDOM_PRIVATE_RESOLVABLE) { + /* Test if there is a matching IRK for this private resolvable address */ + for (uint8_t j = 0; j < whitelistFromBondTable.irk_count; ++j) { + if (securityManager.matchAddressAndIrk(&whitelistAddresses[i], whitelistFromBondTable.pp_irks[j])) { + /* Found the corresponding IRK, add it to our local whitelist */ + whitelist.pp_irks[whitelist.irk_count] = whitelistFromBondTable.pp_irks[j]; + whitelist.irk_count++; + /* Make sure we do not look at this IRK again */ + if (j != whitelistFromBondTable.irk_count - 1) { + /** + * This is not the last IRK, so replace the pointer + * with the last pointer in the array + */ + whitelistFromBondTable.pp_irks[j] = + whitelistFromBondTable.pp_irks[whitelistFromBondTable.irk_count - 1]; + } + /** + * If the IRK is the last pointer in the array simply + * decrement the total IRK count + */ + whitelistFromBondTable.irk_count--; + break; + } + } + } else { + /* Include the address into the whitelist */ + whitelist.pp_addrs[whitelist.addr_count] = &whitelistAddresses[i]; + whitelist.addr_count++; + } + } + + return BLE_ERROR_NONE; } -///////////////////////// diff --git a/source/nRF5xGap.h b/source/nRF5xGap.h index 94a7805..751133b 100644 --- a/source/nRF5xGap.h +++ b/source/nRF5xGap.h @@ -28,6 +28,12 @@ #undef YOTTA_CFG_WHITELIST_MAX_SIZE #define YOTTA_CFG_WHITELIST_MAX_SIZE BLE_GAP_WHITELIST_ADDR_MAX_COUNT #endif +#ifndef YOTTA_CFG_IRK_TABLE_MAX_SIZE + #define YOTTA_CFG_IRK_TABLE_MAX_SIZE BLE_GAP_WHITELIST_IRK_MAX_COUNT +#elif YOTTA_CFG_IRK_TABLE_MAX_SIZE > BLE_GAP_WHITELIST_IRK_MAX_COUNT + #undef YOTTA_CFG_IRK_TABLE_MAX_SIZE + #define YOTTA_CFG_IRK_TABLE_MAX_SIZE BLE_GAP_WHITELIST_IRK_MAX_COUNT +#endif #include "ble/blecommon.h" #include "ble.h" #include "ble/GapAdvertisingParams.h" @@ -43,8 +49,6 @@ extern "C" { #include "btle_security.h" -#include - void radioNotificationStaticCallback(bool param); /**************************************************************************/ @@ -88,19 +92,16 @@ public: virtual ble_error_t reset(void); - /////////////////// WHITELISTING - virtual int8_t getMaxWhitelistSize(void) const; - virtual ble_error_t getWhitelist(std::set &whitelist) const; - virtual ble_error_t setWhitelist(std::set whitelist); + virtual uint8_t getMaxWhitelistSize(void) const; + virtual ble_error_t getWhitelist(Gap::Whitelist_t &whitelistOut) const; + virtual ble_error_t setWhitelist(const Gap::Whitelist_t &whitelistIn); - // Accessors - virtual void setAdvertisingPolicyMode(AdvertisingPolicyMode_t mode); - virtual void setScanningPolicyMode(ScanningPolicyMode_t mode); - virtual void setInitiatorPolicyMode(InitiatorPolicyMode_t mode); + virtual ble_error_t setAdvertisingPolicyMode(AdvertisingPolicyMode_t mode); + virtual ble_error_t setScanningPolicyMode(ScanningPolicyMode_t mode); + virtual ble_error_t setInitiatorPolicyMode(InitiatorPolicyMode_t mode); virtual Gap::AdvertisingPolicyMode_t getAdvertisingPolicyMode(void) const; virtual Gap::ScanningPolicyMode_t getScanningPolicyMode(void) const; virtual Gap::InitiatorPolicyMode_t getInitiatorPolicyMode(void) const; - /////////////////// virtual ble_error_t initRadioNotification(void) { if (ble_radio_notification_init(NRF_APP_PRIORITY_HIGH, NRF_RADIO_NOTIFICATION_DISTANCE_800US, radioNotificationStaticCallback) == NRF_SUCCESS) { @@ -112,47 +113,31 @@ public: /* Observer role is not supported by S110, return BLE_ERROR_NOT_IMPLEMENTED */ #if !defined(TARGET_MCU_NRF51_16K_S110) && !defined(TARGET_MCU_NRF51_32K_S110) - virtual ble_error_t startRadioScan(const GapScanningParams &scanningParams) { - ble_gap_scan_params_t scanParams = { - .active = scanningParams.getActiveScanning(), /**< If 1, perform active scanning (scan requests). */ - .selective = scanningPolicyMode, /**< If 1, ignore unknown devices (non whitelisted). */ - .p_whitelist = &whitelist, /**< Pointer to whitelist, NULL if none is given. */ - .interval = scanningParams.getInterval(), /**< Scan interval between 0x0004 and 0x4000 in 0.625ms units (2.5ms to 10.24s). */ - .window = scanningParams.getWindow(), /**< Scan window between 0x0004 and 0x4000 in 0.625ms units (2.5ms to 10.24s). */ - .timeout = scanningParams.getTimeout(), /**< Scan timeout between 0x0001 and 0xFFFF in seconds, 0x0000 disables timeout. */ - }; - - if (sd_ble_gap_scan_start(&scanParams) != NRF_SUCCESS) { - return BLE_ERROR_PARAM_OUT_OF_RANGE; - } - - return BLE_ERROR_NONE; - } - - virtual ble_error_t stopScan(void) { - if (sd_ble_gap_scan_stop() == NRF_SUCCESS) { - return BLE_ERROR_NONE; - } - - return BLE_STACK_BUSY; - } + virtual ble_error_t startRadioScan(const GapScanningParams &scanningParams); + virtual ble_error_t stopScan(void); #endif private: - /////////////////WHITELISTING + /* Policy modes set by the user. By default these are set to ignore the whitelist */ Gap::AdvertisingPolicyMode_t advertisingPolicyMode; Gap::ScanningPolicyMode_t scanningPolicyMode; - Gap::InitiatorPolicyMode_t initiatorPolicyMode; /* Unused */ - ble_gap_addr_t whitelistAddrs[YOTTA_CFG_WHITELIST_MAX_SIZE]; - ble_gap_addr_t *whitelistAddresses[YOTTA_CFG_WHITELIST_MAX_SIZE]; - uint8_t whitelistAddressesSize; - ble_gap_irk_t *whitelistIrks[YOTTA_CFG_WHITELIST_MAX_SIZE]; - uint8_t whitelistIrksSize; + /* Internal representation of a whitelist */ + uint8_t whitelistAddressesSize; + ble_gap_addr_t whitelistAddresses[YOTTA_CFG_WHITELIST_MAX_SIZE]; + ble_gap_addr_t *whitelistAddressPtrs[YOTTA_CFG_WHITELIST_MAX_SIZE]; + ble_gap_irk_t *whitelistIrkPtrs[YOTTA_CFG_IRK_TABLE_MAX_SIZE]; + /* Structure used by the SoftDevice to represent a whitelist together with IRK table */ ble_gap_whitelist_t whitelist; - ///////////////// + /* + * An internal function used to populate the ble_gap_whitelist_t that will be used by + * the SoftDevice for filtering requests. This function is needed because for the BLE + * API the whitelist is just a collection of keys, but for the stack it also includes + * the IRK table. + */ + ble_error_t generateStackWhitelist(void); bool radioNotificationCallbackParam; /* parameter to be passed into the Timeout-generated radio notification callback. */ Timeout radioNotificationTimeout; @@ -245,17 +230,15 @@ private: nRF5xGap() : advertisingPolicyMode(Gap::ADV_POLICY_IGNORE_WHITELIST), - scanningPolicyMode(Gap::SCAN_POLICY_IGNORE_WHITELIST), - initiatorPolicyMode(Gap::INIT_POLICY_IGNORE_WHITELIST), - whitelistAddressesSize(0), - whitelistIrksSize(0) { + scanningPolicyMode(Gap::SCAN_POLICY_IGNORE_WHITELIST) { m_connectionHandle = BLE_CONN_HANDLE_INVALID; - whitelist.pp_addrs = whitelistAddresses; - for (int i = 0; i < YOTTA_CFG_WHITELIST_MAX_SIZE; i++) { - whitelistAddresses[i] = &(whitelistAddrs[i]); - } - whitelist.pp_irks = whitelistIrks; + /* Reset the whitelist */ + whitelist.addr_count = 0; + whitelist.irk_count = 0; + whitelist.pp_irks = whitelistIrkPtrs; + whitelist.pp_addrs = whitelistAddressPtrs; + whitelistAddressesSize = 0; } nRF5xGap(nRF5xGap const &); diff --git a/source/nRF5xSecurityManager.h b/source/nRF5xSecurityManager.h index 4c30841..2e842b6 100644 --- a/source/nRF5xSecurityManager.h +++ b/source/nRF5xSecurityManager.h @@ -19,6 +19,7 @@ #include +#include "nRF5xGap.h" #include "ble/SecurityManager.h" #include "btle_security.h" @@ -60,6 +61,10 @@ public: return BLE_ERROR_NONE; } + bool hasInitialized(void) const { + return btle_hasInitializedSecurity(); + } + public: /* * Allow instantiation from nRF5xn when required. @@ -73,6 +78,15 @@ public: private: nRF5xSecurityManager(const nRF5xSecurityManager &); const nRF5xSecurityManager& operator=(const nRF5xSecurityManager &); + + ble_error_t createWhitelistFromBondTable(ble_gap_whitelist_t &whitelistFromBondTable) const { + return btle_createWhitelistFromBondTable(&whitelistFromBondTable); + } + + bool matchAddressAndIrk(ble_gap_addr_t *address, ble_gap_irk_t *irk) const { + return btle_matchAddressAndIrk(address, irk); + } + friend class nRF5xGap; }; #endif // ifndef __NRF51822_SECURITY_MANAGER_H__