/** * @file * @author chipsea * @brief * @version 0.1 * @date 2020-11-30 * @copyright Copyright (c) 2020, CHIPSEA Co., Ltd. * @note */ /************************************************************************************************** Filename: gapbondmgr.c Revised: Revision: Description: GAP peripheral profile manages bonded connections **************************************************************************************************/ #include "sdk_config.h" #include "bcomdef.h" #if ( HOST_CONFIG & ( CENTRAL_CFG | PERIPHERAL_CFG ) ) /********************************************************************* * INCLUDES */ #include "OSAL.h" #include "osal_snv.h" #include "gap.h" #include "linkdb.h" #include "gatt.h" #include "gatt_uuid.h" #include "hci.h" #include "gattservapp.h" #include "gapgattserver.h" #include "gapbondmgr.h" // temp set // #define osal_snv_write( a, b, c ) (1) // #define osal_snv_read( a, b, c ) (1) // #define osal_snv_compact( a ) (1) #define BOND_LOG(...) /********************************************************************* * MACROS */ /********************************************************************* * CONSTANTS */ // Task event types #define GAP_BOND_SYNC_CC_EVT 0x0001 // Sync char config #define GAP_BOND_SAVE_REC_EVT 0x0002 // Save bond record in NV // Once NV usage reaches this percentage threshold, NV compaction gets triggered. #define NV_COMPACT_THRESHOLD 80 // Bonded State Flags #define GAP_BONDED_STATE_AUTHENTICATED 0x0001 #define GAP_BONDED_STATE_SERVICE_CHANGED 0x0002 /** * GAP Bond Manager NV layout * * The NV definitions: * BLE_NVID_GAP_BOND_START - starting NV ID * GAP_BONDINGS_MAX - Maximum number of bonding allowed (10 is max for number of NV IDs allocated in bcomdef.h). * * A single bonding entry consists of 6 components (NV items): * Bond Record - defined as gapBondRec_t and uses GAP_BOND_REC_ID_OFFSET for an NV ID * local LTK Info - defined as gapBondLTK_t and uses GAP_BOND_LOCAL_LTK_OFFSET for an NV ID * device LTK Info - defined as gapBondLTK_t and uses GAP_BOND_DEV_LTK_OFFSET for an NV ID * device IRK - defined as "uint8 devIRK[KEYLEN]" and uses GAP_BOND_DEV_IRK_OFFSET for an NV ID * device CSRK - defined as "uint8 devCSRK[KEYLEN]" and uses GAP_BOND_DEV_CSRK_OFFSET for an NV ID * device Sign Counter - defined as a uint32 and uses GAP_BOND_DEV_SIGN_COUNTER_OFFSET for an NV ID * * When the device is initialized for the first time, all (GAP_BONDINGS_MAX) NV items are created and * initialized to all 0xFF's. A bonding record of all 0xFF's indicates that the bonding record is empty * and free to use. * * The calculation for each bonding records NV IDs: * mainRecordNvID = ((bondIdx * GAP_BOND_REC_IDS) + BLE_NVID_GAP_BOND_START) * localLTKNvID = (((bondIdx * GAP_BOND_REC_IDS) + GAP_BOND_LOCAL_LTK_OFFSET) + BLE_NVID_GAP_BOND_START) * */ #define GAP_BOND_REC_ID_OFFSET 0 //!< NV ID for the main bonding record #define GAP_BOND_LOCAL_LTK_OFFSET 1 //!< NV ID for the bonding record's local LTK information #define GAP_BOND_DEV_LTK_OFFSET 2 //!< NV ID for the bonding records' device LTK information #define GAP_BOND_DEV_IRK_OFFSET 3 //!< NV ID for the bonding records' device IRK #define GAP_BOND_DEV_CSRK_OFFSET 4 //!< NV ID for the bonding records' device CSRK #define GAP_BOND_DEV_SIGN_COUNTER_OFFSET 5 //!< NV ID for the bonding records' device Sign Counter #define GAP_BOND_REC_IDS 6 // Macros to calculate the index/offset in to NV space #define calcNvID(Idx, offset) (((((Idx) * GAP_BOND_REC_IDS) + (offset))) + BLE_NVID_GAP_BOND_START) #define mainRecordNvID(bondIdx) (calcNvID((bondIdx), GAP_BOND_REC_ID_OFFSET)) #define localLTKNvID(bondIdx) (calcNvID((bondIdx), GAP_BOND_LOCAL_LTK_OFFSET)) #define devLTKNvID(bondIdx) (calcNvID((bondIdx), GAP_BOND_DEV_LTK_OFFSET)) #define devIRKNvID(bondIdx) (calcNvID((bondIdx), GAP_BOND_DEV_IRK_OFFSET)) #define devCSRKNvID(bondIdx) (calcNvID((bondIdx), GAP_BOND_DEV_CSRK_OFFSET)) #define devSignCounterNvID(bondIdx) (calcNvID((bondIdx), GAP_BOND_DEV_SIGN_COUNTER_OFFSET)) // Macros to calculate the GATT index/offset in to NV space #define gattCfgNvID(Idx) ((Idx) + BLE_NVID_GATT_CFG_START) // Key Size Limits #define MIN_ENC_KEYSIZE 7 //!< Minimum number of bytes for the encryption key #define MAX_ENC_KEYSIZE 16 //!< Maximum number of bytes for the encryption key /********************************************************************* * TYPEDEFS */ // Structure of NV data for the connected device's encryption information typedef struct { uint8 LTK[KEYLEN]; // Long Term Key (LTK) uint16 div; //lint -e754 // LTK eDiv uint8 rand[B_RANDOM_NUM_SIZE]; // LTK random number uint8 keySize; // LTK key size } gapBondLTK_t; // Structure of NV data for the connected device's address information typedef struct { uint8 publicAddr[B_ADDR_LEN]; // Master's address uint8 reconnectAddr[B_ADDR_LEN]; // Privacy Reconnection Address uint16 stateFlags; // State flags: SM_AUTH_STATE_AUTHENTICATED & SM_AUTH_STATE_BONDING } gapBondRec_t; // Structure of NV data for the connected device's characteristic configuration typedef struct { uint16 attrHandle; // attribute handle uint8 value; // attribute value for this device } gapBondCharCfg_t; /********************************************************************* * GLOBAL VARIABLES */ /********************************************************************* * EXTERNAL VARIABLES */ /********************************************************************* * EXTERNAL FUNCTIONS */ /********************************************************************* * LOCAL VARIABLES */ static uint8 gapBondMgr_TaskID = INVALID_TASK_ID; // Task ID for internal task/event processing // GAPBonding Parameters static uint8 gapBond_PairingMode[MAX_NUM_LL_CONN] = {GAPBOND_PAIRING_MODE_WAIT_FOR_REQ}; static uint16 gapBond_InitiateWait = 1000; // Default to 1 second static uint8 gapBond_MITM = FALSE; static uint8 gapBond_IOCap = GAPBOND_IO_CAP_DISPLAY_ONLY; static uint8 gapBond_OOBDataFlag = FALSE; static uint8 gapBond_OOBData[KEYLEN] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; static uint8 gapBond_Bonding = FALSE; // 2020-04-22 support secure connection pairing static uint8 gapBond_SC = FALSE; static uint8 gapBond_keyPress = FALSE; static uint8 gapBond_CT2 = FALSE; static uint8 gapBond_AutoFail = FALSE; static uint8 gapBond_AutoFailReason = SMP_PAIRING_FAILED_NOT_SUPPORTED; static uint8 gapBond_KeyDistList = ( GAPBOND_KEYDIST_SENCKEY // sEncKey enabled, to send the encryption key | GAPBOND_KEYDIST_SIDKEY // sIdKey enabled, to send the IRK, and BD_ADDR | GAPBOND_KEYDIST_SSIGN // sSign enabled, to send the CSRK | GAPBOND_KEYDIST_MENCKEY // mEncKey enabled, to get the master's encryption key | GAPBOND_KEYDIST_MIDKEY // mIdKey enabled, to get the master's IRK and BD_ADDR | GAPBOND_KEYDIST_MSIGN // mSign enabled, to get the master's CSRK ); static uint32 gapBond_Passcode = 0; static uint8 gapBond_KeySize = MAX_ENC_KEYSIZE; #if (GAP_BOND_MGR_INDEX_REPLACE) static uint8 bondReplaceCnt=0; #endif #if ( HOST_CONFIG & CENTRAL_CFG ) static uint8 gapBond_BondFailOption = GAPBOND_FAIL_TERMINATE_LINK; #endif static const gapBondCBs_t *pGapBondCB = NULL; // Local RAM shadowed bond records static gapBondRec_t bonds[GAP_BONDINGS_MAX] = {0}; static uint8 autoSyncWhiteList = FALSE; static uint8 eraseAllBonds = FALSE; static uint8 bondsToDelete[GAP_BONDINGS_MAX] = {FALSE}; // Globals used for saving bond record and CCC values in NV static uint8 bondIdx = GAP_BONDINGS_MAX; static gapAuthCompleteEvent_t *pAuthEvt[MAX_NUM_LL_CONN] = {NULL}; /********************************************************************* * LOCAL FUNCTIONS */ static uint8 gapBondMgrChangeState( uint8 idx, uint16 state, uint8 set ); static uint8 gapBondMgrUpdateCharCfg( uint8 idx, uint16 attrHandle, uint16 value ); static gapBondCharCfg_t *gapBondMgrFindCharCfgItem( uint16 attrHandle, gapBondCharCfg_t *charCfgTbl ); static void gapBondMgrInvertCharCfgItem( gapBondCharCfg_t *charCfgTbl ); static uint8 gapBondMgrAddBond( gapBondRec_t *pBondRec, gapAuthCompleteEvent_t *pPkt ); static uint8 gapBondMgrGetStateFlags( uint8 idx ); static bStatus_t gapBondMgrGetPublicAddr( uint8 idx, uint8 *pAddr ); static uint8 gapBondMgrFindReconnectAddr( uint8 *pReconnectAddr ); static uint8 gapBondMgrFindAddr( uint8 *pDevAddr ); static uint8 gapBondMgrResolvePrivateAddr( uint8 *pAddr ); static void gapBondMgrReadBonds( void ); static uint8 gapBondMgrFindEmpty( void ); static uint8 gapBondMgrBondTotal( void ); static bStatus_t gapBondMgrEraseAllBondings( void ); static bStatus_t gapBondMgrEraseBonding( uint8 idx ); static uint8 gapBondMgr_ProcessOSALMsg( osal_event_hdr_t *pMsg ); static void gapBondMgrSendServiceChange( linkDBItem_t *pLinkItem ); static void gapBondMgr_ProcessGATTMsg( gattMsgEvent_t *pMsg ); static void gapBondMgr_ProcessGATTServMsg( gattEventHdr_t *pMsg ); static void gapBondSetupPrivFlag( void ); static void gapBondMgrBondReq( uint16 connHandle, uint8 idx, uint8 stateFlags, uint8 role, uint8 startEncryption ); static void gapBondMgrAuthenticate( uint16 connHandle, uint8 addrType, gapPairingReq_t *pPairReq ); static void gapBondMgr_SyncWhiteList( void ); static uint8 gapBondMgr_SyncCharCfg( uint16 connHandle ); static void gapBondFreeAuthEvt( uint16 connHandle ); static void gapBondRecvEvt(uint16 connHandle,gapBondRec_t* pBondRec, gapAuthCompleteEvent_t* pPkt ); #if ( HOST_CONFIG & PERIPHERAL_CFG ) static void gapBondMgrSlaveSecurityReq( uint16 connHandle ); #endif /********************************************************************* * NETWORK LAYER CALLBACKS */ /********************************************************************* * PUBLIC FUNCTIONS */ static void gapBondRecvEvt(uint16 connHandle, gapBondRec_t* pBondRec, gapAuthCompleteEvent_t* pPkt ) { if ( gapBondMgrAddBond( pBondRec, pPkt ) ) { // Update NV to have same CCC values as GATT database // Note: pAuthEvt is a global variable used for deferring the storage if ( gapBondMgr_SyncCharCfg( pAuthEvt[connHandle]->connectionHandle ) ) { if ( pGapBondCB && pGapBondCB->pairStateCB ) { // Assume SUCCESS since we got this far. pGapBondCB->pairStateCB( pAuthEvt[connHandle]->connectionHandle, GAPBOND_PAIRING_STATE_COMPLETE, SUCCESS ); } // We're done storing bond record and CCC values in NV gapBondFreeAuthEvt(connHandle); } } } /********************************************************************* * @brief Set a GAP Bond Manager parameter. * * Public function defined in gapbondmgr.h. */ bStatus_t GAPBondMgr_SetParameter( uint16 param, uint8 len, void *pValue ) { bStatus_t ret = SUCCESS; // return value switch ( param ) { case GAPBOND_PAIRING_MODE: if ( (len == sizeof ( uint8 )) && (*((uint8*)pValue) <= GAPBOND_PAIRING_MODE_INITIATE) ) { // gapBond_PairingMode = *((uint8*)pValue); osal_memset(gapBond_PairingMode,*((uint8*)pValue),sizeof(gapBond_PairingMode)); } else { ret = bleInvalidRange; } break; case GAPBOND_INITIATE_WAIT: if ( len == sizeof ( uint16 ) ) { gapBond_InitiateWait = *((uint16*)pValue); } else { ret = bleInvalidRange; } break; case GAPBOND_MITM_PROTECTION: if ( (len == sizeof ( uint8 )) && (*((uint8*)pValue) <= TRUE) ) { gapBond_MITM = *((uint8*)pValue); } else { ret = bleInvalidRange; } break; case GAPBOND_IO_CAPABILITIES: if ( (len == sizeof ( uint8 )) && (*((uint8*)pValue) <= GAPBOND_IO_CAP_KEYBOARD_DISPLAY) ) { gapBond_IOCap = *((uint8*)pValue); } else { ret = bleInvalidRange; } break; case GAPBOND_OOB_ENABLED: if ( (len == sizeof ( uint8 )) && (*((uint8*)pValue) <= TRUE) ) { gapBond_OOBDataFlag = *((uint8*)pValue); } else { ret = bleInvalidRange; } break; case GAPBOND_OOB_DATA: if ( len == KEYLEN ) { VOID osal_memcpy( gapBond_OOBData, pValue, KEYLEN ) ; } else { ret = bleInvalidRange; } break; case GAPBOND_BONDING_ENABLED: if ( (len == sizeof ( uint8 )) && (*((uint8*)pValue) <= TRUE) ) { gapBond_Bonding = *((uint8*)pValue); } else { ret = bleInvalidRange; } break; case GAPBOND_KEY_DIST_LIST: if ( len == sizeof ( uint8 ) ) { gapBond_KeyDistList = *((uint8*)pValue); } else { ret = bleInvalidRange; } break; case GAPBOND_DEFAULT_PASSCODE: if ( (len == sizeof ( uint32 )) && (*((uint32*)pValue) <= GAP_PASSCODE_MAX) ) { gapBond_Passcode = *((uint32*)pValue); } else { ret = bleInvalidRange; } break; case GAPBOND_ERASE_ALLBONDS: if ( len == 0 ) { // Make sure there's no active connection if ( GAP_NumActiveConnections() == 0 ) { // Erase all bonding records VOID gapBondMgrEraseAllBondings(); // See if NV needs a compaction VOID osal_snv_compact( NV_COMPACT_THRESHOLD ); // Make sure Bond RAM Shadow is up-to-date gapBondMgrReadBonds(); } else { eraseAllBonds = TRUE; } } else { ret = bleInvalidRange; } break; case GAPBOND_ERASE_SINGLEBOND: if ( len == (1 + B_ADDR_LEN) ) { uint8 idx; uint8 devAddr[B_ADDR_LEN]; // Reverse bytes VOID osal_revmemcpy( devAddr, (uint8 *)pValue+1, B_ADDR_LEN ); // Resolve address and find index idx = GAPBondMgr_ResolveAddr( *((uint8 *)pValue), devAddr, NULL ); if ( idx < GAP_BONDINGS_MAX ) { // Make sure there's no active connection if ( GAP_NumActiveConnections() == 0 ) { // Erase bond gapBondMgrEraseBonding( idx ); // See if NV needs a compaction VOID osal_snv_compact( NV_COMPACT_THRESHOLD ); // Make sure Bond RAM Shadow is up-to-date gapBondMgrReadBonds(); } else { // Mark entry to be deleted when disconnected bondsToDelete[idx] = TRUE; } } else { ret = INVALIDPARAMETER; } } else { // Parameter is not the correct length ret = bleInvalidRange; } break; case GAPBOND_AUTO_FAIL_PAIRING: if ( (len == sizeof ( uint8 )) && (*((uint8*)pValue) <= TRUE) ) { gapBond_AutoFail = *((uint8*)pValue); } else { ret = bleInvalidRange; } break; case GAPBOND_AUTO_FAIL_REASON: if ( (len == sizeof ( uint8 )) && (*((uint8*)pValue) <= SMP_PAIRING_FAILED_REPEATED_ATTEMPTS) ) { gapBond_AutoFailReason = *((uint8*)pValue); } else { ret = bleInvalidRange; } break; case GAPBOND_KEYSIZE: if ( (len == sizeof ( uint8 )) && ((*((uint8*)pValue) >= MIN_ENC_KEYSIZE) && (*((uint8*)pValue) <= MAX_ENC_KEYSIZE)) ) { gapBond_KeySize = *((uint8*)pValue); } else { ret = bleInvalidRange; } break; case GAPBOND_AUTO_SYNC_WL: if ( len == sizeof( uint8 ) ) { uint8 oldVal = autoSyncWhiteList; autoSyncWhiteList = *((uint8 *)pValue); // only call if parameter changes from FALSE to TRUE if ( ( oldVal == FALSE ) && ( autoSyncWhiteList == TRUE ) ) { // make sure bond is updated from NV gapBondMgrReadBonds(); } } else { ret = bleInvalidRange; } break; #if ( HOST_CONFIG & CENTRAL_CFG ) case GAPBOND_BOND_FAIL_ACTION: if ( (len == sizeof ( uint8 )) && (*((uint8*)pValue) <= GAPBOND_FAIL_TERMINATE_ERASE_BONDS) ) { gapBond_BondFailOption = *((uint8*)pValue); } else { ret = bleInvalidRange; } break; #endif default: // The param value isn't part of this profile, try the GAP. if ( (param < TGAP_PARAMID_MAX) && (len == sizeof ( uint16 )) ) { ret = GAP_SetParamValue( param, *((uint16*)pValue) ); } else { ret = INVALIDPARAMETER; } break; } return ( ret ); } /********************************************************************* * @brief Get a GAP Bond Manager parameter. * * Public function defined in gapbondmgr.h. */ bStatus_t GAPBondMgr_GetParameter( uint16 param, void *pValue ) { bStatus_t ret = SUCCESS; // return value switch ( param ) { case GAPBOND_PAIRING_MODE: // *((uint8*)pValue) = gapBond_PairingMode; break; case GAPBOND_INITIATE_WAIT: *((uint16*)pValue) = gapBond_InitiateWait; break; case GAPBOND_MITM_PROTECTION: *((uint8*)pValue) = gapBond_MITM; break; case GAPBOND_IO_CAPABILITIES: *((uint8*)pValue) = gapBond_IOCap; break; case GAPBOND_OOB_ENABLED: *((uint8*)pValue) = gapBond_OOBDataFlag; break; case GAPBOND_OOB_DATA: VOID osal_memcpy( pValue, gapBond_OOBData, KEYLEN ) ; break; case GAPBOND_BONDING_ENABLED: *((uint8*)pValue) = gapBond_Bonding; break; case GAPBOND_KEY_DIST_LIST: *((uint8*)pValue) = gapBond_KeyDistList; break; case GAPBOND_DEFAULT_PASSCODE: *((uint32*)pValue) = gapBond_Passcode; break; case GAPBOND_AUTO_FAIL_PAIRING: *((uint8*)pValue) = gapBond_AutoFail; break; case GAPBOND_AUTO_FAIL_REASON: *((uint8*)pValue) = gapBond_AutoFailReason; break; case GAPBOND_KEYSIZE: *((uint8*)pValue) = gapBond_KeySize; break; case GAPBOND_AUTO_SYNC_WL: *((uint8*)pValue) = autoSyncWhiteList; break; case GAPBOND_BOND_COUNT: *((uint8*)pValue) = gapBondMgrBondTotal(); break; default: // The param value isn't part of this profile, try the GAP. if ( param < TGAP_PARAMID_MAX ) { *((uint16*)pValue) = GAP_GetParamValue( param ); } else { ret = INVALIDPARAMETER; } break; } return ( ret ); } /********************************************************************* * @brief Notify the Bond Manager that a connection has been made. * * Public function defined in gapbondmgr.h. */ bStatus_t GAPBondMgr_LinkEst( uint8 addrType, uint8 *pDevAddr, uint16 connHandle, uint8 role ) { uint8 idx; // NV Index uint8 publicAddr[B_ADDR_LEN] // Place to put the public address = {0, 0, 0, 0, 0, 0}; idx = GAPBondMgr_ResolveAddr( addrType, pDevAddr, publicAddr ); if ( idx < GAP_BONDINGS_MAX ) { uint8 stateFlags = gapBondMgrGetStateFlags( idx ); smSigningInfo_t signingInfo; gapBondCharCfg_t charCfg[GAP_CHAR_CFG_MAX]; // Space to read a char cfg record from NV // On peripheral, load the key information for the bonding // On central and initiaiting security, load key to initiate encyption gapBondMgrBondReq( connHandle, idx, stateFlags, role, ((role == GAP_PROFILE_CENTRAL ) ? TRUE : FALSE) ); // Load the Signing Key VOID osal_memset( &signingInfo, 0, sizeof ( smSigningInfo_t ) ); if ( osal_snv_read( devCSRKNvID(idx), KEYLEN, signingInfo.srk ) == SUCCESS ) { if ( osal_isbufset( signingInfo.srk, 0xFF, KEYLEN ) == FALSE ) { // Load the signing information for this connection VOID osal_snv_read( devSignCounterNvID(idx), sizeof ( uint32 ), &(signingInfo.signCounter) ); VOID GAP_Signable( connHandle, ((stateFlags & GAP_BONDED_STATE_AUTHENTICATED) ? TRUE : FALSE), &signingInfo ); } } // Load the characteristic configuration if ( osal_snv_read( gattCfgNvID(idx), sizeof ( charCfg ), charCfg ) == SUCCESS ) { gapBondMgrInvertCharCfgItem( charCfg ); for ( uint8 i = 0; i < GAP_CHAR_CFG_MAX; i++ ) { gapBondCharCfg_t *pItem = &(charCfg[i]); // Apply the characteristic configuration for this connection if ( pItem->attrHandle != GATT_INVALID_HANDLE ) { VOID GATTServApp_UpdateCharCfg( connHandle, pItem->attrHandle, (uint16)(pItem->value) ); } } } // Has there been a service change? if ( stateFlags & GAP_BONDED_STATE_SERVICE_CHANGED ) { VOID GATTServApp_SendServiceChangedInd( connHandle, gapBondMgr_TaskID ); } } #if ( HOST_CONFIG & CENTRAL_CFG ) else if ( role == GAP_PROFILE_CENTRAL && gapBond_PairingMode[connHandle] == GAPBOND_PAIRING_MODE_INITIATE ) { // If Central and initiating and not bonded, then initiate pairing gapBondMgrAuthenticate( connHandle, addrType, NULL ); // Call app state callback if ( pGapBondCB && pGapBondCB->pairStateCB ) { pGapBondCB->pairStateCB( connHandle, GAPBOND_PAIRING_STATE_STARTED, SUCCESS ); } } #endif #if ( HOST_CONFIG & PERIPHERAL_CFG ) // If Peripheral and initiating, send a slave security request to // initiate either pairing or encryption if ( role == GAP_PROFILE_PERIPHERAL && gapBond_PairingMode[connHandle] == GAPBOND_PAIRING_MODE_INITIATE ) { gapBondMgrSlaveSecurityReq( connHandle ); } #endif return ( SUCCESS ); } /********************************************************************* * @brief Resolve an address from bonding information. * * Public function defined in gapbondmgr.h. */ uint8 GAPBondMgr_ResolveAddr( uint8 addrType, uint8 *pDevAddr, uint8 *pResolvedAddr ) { uint8 idx = GAP_BONDINGS_MAX; switch ( addrType ) { case ADDRTYPE_PUBLIC: case ADDRTYPE_STATIC: idx = gapBondMgrFindAddr( pDevAddr ); if ( (idx < GAP_BONDINGS_MAX) && (pResolvedAddr) ) { VOID osal_memcpy( pResolvedAddr, pDevAddr, B_ADDR_LEN ); } break; case ADDRTYPE_PRIVATE_NONRESOLVE: // This could be a reconnection address idx = gapBondMgrFindReconnectAddr( pDevAddr ); if ( (idx < GAP_BONDINGS_MAX) && (pResolvedAddr) ) { VOID gapBondMgrGetPublicAddr( idx, pResolvedAddr ); } break; case ADDRTYPE_PRIVATE_RESOLVE: // Master's don't use Private Resolvable addresses but just in case idx = gapBondMgrResolvePrivateAddr( pDevAddr ); if ( (idx < GAP_BONDINGS_MAX) && (pResolvedAddr) ) { VOID gapBondMgrGetPublicAddr( idx, pResolvedAddr ); } break; default: break; } return ( idx ); } /********************************************************************* * @brief Set/clear the service change indication in a bond record. * * Public function defined in gapbondmgr.h. */ bStatus_t GAPBondMgr_ServiceChangeInd( uint16 connectionHandle, uint8 setParam ) { bStatus_t ret = bleNoResources; // return value if ( connectionHandle == 0xFFFF ) { uint8 idx; // loop counter // Run through the bond database and update the Service Change indication for ( idx = 0; idx < GAP_BONDINGS_MAX; idx++ ) { if ( gapBondMgrChangeState( idx, GAP_BONDED_STATE_SERVICE_CHANGED, setParam ) ) { ret = SUCCESS; } } // If the service change indication is TRUE, tell the connected devices if ( setParam ) { // Run connected database linkDB_PerformFunc( gapBondMgrSendServiceChange ); } } else { // Find connection information linkDBItem_t *pLinkItem = linkDB_Find( connectionHandle ); if ( pLinkItem ) { uint8 idx; // loop counter idx = GAPBondMgr_ResolveAddr( pLinkItem->addrType, pLinkItem->addr, NULL ); if ( idx < GAP_BONDINGS_MAX ) { // Bond found, update it. VOID gapBondMgrChangeState( idx, GAP_BONDED_STATE_SERVICE_CHANGED, setParam ); ret = SUCCESS; } // If the service change indication is TRUE, tell the connected device if ( setParam ) { gapBondMgrSendServiceChange( pLinkItem ); } } else { ret = bleNotConnected; } } return ( ret ); } /********************************************************************* * @brief Update the Characteristic Configuration in a bond record. * * Public function defined in gapbondmgr.h. */ bStatus_t GAPBondMgr_UpdateCharCfg( uint16 connectionHandle, uint16 attrHandle, uint16 value ) { bStatus_t ret = bleNoResources; // return value if ( connectionHandle == INVALID_CONNHANDLE ) { uint8 idx; // loop counter // Run through the bond database and update the Characteristic Configuration for ( idx = 0; idx < GAP_BONDINGS_MAX; idx++ ) { if ( gapBondMgrUpdateCharCfg( idx, attrHandle, value ) ) { ret = SUCCESS; } } } else { // Find connection information linkDBItem_t *pLinkItem = linkDB_Find( connectionHandle ); if ( pLinkItem ) { uint8 idx = GAPBondMgr_ResolveAddr( pLinkItem->addrType, pLinkItem->addr, NULL ); if ( idx < GAP_BONDINGS_MAX ) { // Bond found, update it. VOID gapBondMgrUpdateCharCfg( idx, attrHandle, value ); ret = SUCCESS; } } else { ret = bleNotConnected; } } return ( ret ); } /********************************************************************* * @brief Register callback functions with the bond manager. * * Public function defined in gapbondmgr.h. */ void GAPBondMgr_Register( gapBondCBs_t *pCB ) { pGapBondCB = pCB; if(gapBondMgr_TaskID != INVALID_TASK_ID) { // Take over the processing of Authentication messages VOID GAP_SetParamValue( TGAP_AUTH_TASK_ID, gapBondMgr_TaskID ); // Register with GATT Server App for event messages GATTServApp_RegisterForMsg( gapBondMgr_TaskID ); } } /********************************************************************* * @brief Respond to a passcode request. * * Public function defined in gapbondmgr.h. */ bStatus_t GAPBondMgr_PasscodeRsp( uint16 connectionHandle, uint8 status, uint32 passcode ) { bStatus_t ret = SUCCESS; if ( status == SUCCESS ) { // Truncate the passcode passcode = passcode % (GAP_PASSCODE_MAX + 1); ret = GAP_PasscodeUpdate( passcode, connectionHandle ); if ( ret != SUCCESS ) { VOID GAP_TerminateAuth( connectionHandle, SMP_PAIRING_FAILED_PASSKEY_ENTRY_FAILED ); } } else { VOID GAP_TerminateAuth( connectionHandle, status ); } return ret; } /********************************************************************* * @brief This is a bypass mechanism to allow the bond manager to process * GAP messages. * * Public function defined in gapbondmgr.h. */ uint8 GAPBondMgr_ProcessGAPMsg( gapEventHdr_t *pMsg ) { switch ( pMsg->opcode ) { case GAP_PASSKEY_NEEDED_EVENT: { gapPasskeyNeededEvent_t *pPkt = (gapPasskeyNeededEvent_t *)pMsg; if ( pGapBondCB && pGapBondCB->passcodeCB ) { // Ask app for a passcode pGapBondCB->passcodeCB( pPkt->deviceAddr, pPkt->connectionHandle, pPkt->uiInputs, pPkt->uiOutputs ); } else { // No app support, use the default passcode if ( GAP_PasscodeUpdate( gapBond_Passcode, pPkt->connectionHandle ) != SUCCESS ) { VOID GAP_TerminateAuth( pPkt->connectionHandle, SMP_PAIRING_FAILED_PASSKEY_ENTRY_FAILED ); } } } break; case GAP_AUTHENTICATION_COMPLETE_EVENT: { gapAuthCompleteEvent_t *pPkt = (gapAuthCompleteEvent_t *)pMsg; // Should we save bonding information (one save at a time) if ( (pPkt->hdr.status == SUCCESS) && (pPkt->authState & SM_AUTH_STATE_BONDING) && (pAuthEvt[pPkt->connectionHandle] == NULL) ) { gapBondRec_t bondRec; VOID osal_memset( &bondRec, 0, sizeof ( gapBondRec_t ) ) ; // Do we have a public address in the data? if ( pPkt->pIdentityInfo ) { VOID osal_memcpy( bondRec.publicAddr, pPkt->pIdentityInfo->bd_addr, B_ADDR_LEN ); } else { linkDBItem_t *pLinkItem = linkDB_Find( pPkt->connectionHandle ); if ( pLinkItem ) { VOID osal_memcpy( bondRec.publicAddr, pLinkItem->addr, B_ADDR_LEN ); } else { // We don't have an address, so ignore the message. break; } } // Save off of the authentication state bondRec.stateFlags |= (pPkt->authState & SM_AUTH_STATE_AUTHENTICATED) ? GAP_BONDED_STATE_AUTHENTICATED : 0; if ( !gapBondMgrAddBond( &bondRec, pPkt ) ) { // Notify our task to save bonding information in NV // osal_set_event( gapBondMgr_TaskID, GAP_BOND_SAVE_REC_EVT ); gapBondRecvEvt( pPkt->connectionHandle,&bondRec, pPkt ); // We're not done with this message; it will be freed later return ( FALSE ); } } // Call app state callback in the fail case. Success is handled after GAP_BOND_SAVE_REC_EVT. if ( pGapBondCB && pGapBondCB->pairStateCB ) { pGapBondCB->pairStateCB( pPkt->connectionHandle, GAPBOND_PAIRING_STATE_COMPLETE, pPkt->hdr.status ); } } break; case GAP_BOND_COMPLETE_EVENT: // This message is received when the bonding is complete. If hdr.status is SUCCESS // then call app state callback. If hdr.status is NOT SUCCESS, the connection will be // dropped at the LL because of a MIC failure, so again nothing to do. { gapBondCompleteEvent_t *pPkt = (gapBondCompleteEvent_t *)pMsg; #if ( HOST_CONFIG & CENTRAL_CFG ) if ( pPkt->hdr.status == LL_ENC_KEY_REQ_REJECTED ) { // LTK not found on peripheral device (Pin or Key Missing). See which // option was configured for unsuccessful bonding. linkDBItem_t *pLinkItem = linkDB_Find( pPkt->connectionHandle ); if ( pLinkItem ) { switch ( gapBond_BondFailOption ) { case GAPBOND_FAIL_INITIATE_PAIRING: // Initiate pairing gapBondMgrAuthenticate( pPkt->connectionHandle, pLinkItem->addrType, NULL ); break; case GAPBOND_FAIL_TERMINATE_LINK: // Drop connection GAP_TerminateLinkReq( pLinkItem->taskID, pPkt->connectionHandle, HCI_DISCONNECT_AUTH_FAILURE ); break; case GAPBOND_FAIL_TERMINATE_ERASE_BONDS: // Set up bond manager to erase all existing bonds after connection terminates VOID GAPBondMgr_SetParameter( GAPBOND_ERASE_ALLBONDS, 0, NULL ); // Drop connection GAP_TerminateLinkReq( pLinkItem->taskID, pPkt->connectionHandle, HCI_DISCONNECT_AUTH_FAILURE ); break; case GAPBOND_FAIL_NO_ACTION: // fall through default: // do nothing break; } } } #endif if ( pGapBondCB && pGapBondCB->pairStateCB ) { pGapBondCB->pairStateCB( pPkt->connectionHandle, GAPBOND_PAIRING_STATE_BONDED, pMsg->hdr.status ); } } break; case GAP_SIGNATURE_UPDATED_EVENT: { uint8 idx; gapSignUpdateEvent_t *pPkt = (gapSignUpdateEvent_t *)pMsg; idx = GAPBondMgr_ResolveAddr( pPkt->addrType, pPkt->devAddr, NULL ); if ( idx < GAP_BONDINGS_MAX ) { // Save the sign counter VOID osal_snv_write( devSignCounterNvID(idx), sizeof ( uint32 ), &(pPkt->signCounter) ); } } break; #if ( HOST_CONFIG & PERIPHERAL_CFG ) case GAP_PAIRING_REQ_EVENT: { gapPairingReqEvent_t *pPkt = (gapPairingReqEvent_t *)pMsg; if ( gapBond_AutoFail != FALSE ) { // Auto Fail TEST MODE (DON'T USE THIS) - Sends pre-setup reason VOID GAP_TerminateAuth( pPkt->connectionHandle, gapBond_AutoFailReason ); } else if ( gapBond_PairingMode[pPkt->connectionHandle] == GAPBOND_PAIRING_MODE_NO_PAIRING ) { // No Pairing - Send error VOID GAP_TerminateAuth( pPkt->connectionHandle, SMP_PAIRING_FAILED_NOT_SUPPORTED ); } else { linkDBItem_t *pLinkItem = linkDB_Find( pPkt->connectionHandle ); // Requesting bonding? if ( pPkt->pairReq.authReq & SM_AUTH_STATE_BONDING ) { if ( pLinkItem ) { if ( (pLinkItem->addrType != ADDRTYPE_PUBLIC) && (pPkt->pairReq.keyDist.mIdKey == FALSE) ) { uint8 publicAddr[B_ADDR_LEN]; // Check if we already have the public address in NV if ( GAPBondMgr_ResolveAddr(pLinkItem->addrType, pLinkItem->addr, publicAddr ) == FALSE ) { // Can't bond to a non-public address if we don't know the public address VOID GAP_TerminateAuth( pPkt->connectionHandle, SMP_PAIRING_FAILED_AUTH_REQ ); break; } } } else { // Can't find the connection, ignore the message break; } } // Send pairing response gapBondMgrAuthenticate( pPkt->connectionHandle, pLinkItem->addrType, &(pPkt->pairReq) ); // Call app state callback if ( pGapBondCB && pGapBondCB->pairStateCB ) { pGapBondCB->pairStateCB( pPkt->connectionHandle, GAPBOND_PAIRING_STATE_STARTED, SUCCESS ); } } } break; #endif #if ( HOST_CONFIG & CENTRAL_CFG ) case GAP_SLAVE_REQUESTED_SECURITY_EVENT: { uint16 connHandle = ((gapSlaveSecurityReqEvent_t *)pMsg)->connectionHandle; uint8 idx; uint8 publicAddr[B_ADDR_LEN] = {0, 0, 0, 0, 0, 0}; linkDBItem_t *pLink = linkDB_Find( connHandle ); // If link found and not already initiating security if (pLink != NULL && gapBond_PairingMode[connHandle] != GAPBOND_PAIRING_MODE_INITIATE) { // If already bonded initiate encryption idx = GAPBondMgr_ResolveAddr( pLink->addrType, pLink->addr, publicAddr ); if ( idx < GAP_BONDINGS_MAX ) { gapBondMgrBondReq( connHandle, idx, gapBondMgrGetStateFlags( idx ), GAP_PROFILE_CENTRAL, TRUE ); } // Else if no pairing allowed else if ( gapBond_PairingMode[connHandle] == GAPBOND_PAIRING_MODE_NO_PAIRING ) { // Send error VOID GAP_TerminateAuth( connHandle, SMP_PAIRING_FAILED_NOT_SUPPORTED ); } // Else if waiting for request else if (gapBond_PairingMode[connHandle] == GAPBOND_PAIRING_MODE_WAIT_FOR_REQ) { // Initiate pairing gapBondMgrAuthenticate( connHandle, pLink->addrType, NULL ); } } } break; #endif case GAP_LINK_TERMINATED_EVENT: if ( GAP_NumActiveConnections() == 0 ) { // See if we're asked to erase all bonding records if ( eraseAllBonds == TRUE ) { VOID gapBondMgrEraseAllBondings(); eraseAllBonds = FALSE; // Reset bonds to delete table osal_memset( bondsToDelete, FALSE, sizeof( bondsToDelete ) ); } else { // See if we're asked to erase any single bonding records for (uint8 idx = 0; idx < GAP_BONDINGS_MAX; idx++) { if ( bondsToDelete[idx] == TRUE ) { VOID gapBondMgrEraseBonding( idx ); bondsToDelete[idx] = FALSE; } } } // See if NV needs a compaction VOID osal_snv_compact( NV_COMPACT_THRESHOLD ); // Make sure Bond RAM Shadow is up-to-date gapBondMgrReadBonds(); } break; default: break; } return ( TRUE ); } /********************************************************************* * LOCAL FUNCTION PROTOTYPES */ /********************************************************************* * @fn gapBondMgrChangeState * * @brief Change a state flag in the stateFlags field of the bond record. * * @param idx - Bond NV index * @param state - state flage to set or clear * @param set - TRUE to set the flag, FALSE to clear the flag * * @return TRUE if NV Record exists, FALSE if NV Record is empty */ static uint8 gapBondMgrChangeState( uint8 idx, uint16 state, uint8 set ) { gapBondRec_t bondRec; // Space to read a Bond record from NV // Look for public address that is used (not all 0xFF's) if ( (osal_snv_read( mainRecordNvID(idx), sizeof ( gapBondRec_t ), &bondRec ) == SUCCESS) && (osal_isbufset( bondRec.publicAddr, 0xFF, B_ADDR_LEN ) == FALSE) ) { // Update the state of the bonded device. uint8 stateFlags = bondRec.stateFlags; if ( set ) { stateFlags |= state; } else { stateFlags &= ~(state); } if ( stateFlags != bondRec.stateFlags ) { bondRec.stateFlags = stateFlags; VOID osal_snv_write( mainRecordNvID(idx), sizeof ( gapBondRec_t ), &bondRec ); } return ( TRUE ); } return ( FALSE ); } /********************************************************************* * @fn gapBondMgrUpdateCharCfg * * @brief Update the Characteristic Configuration of the bond record. * * @param idx - Bond NV index * @param attrHandle - attribute handle (0 means all handles) * @param value - characteristic configuration value * * @return TRUE if NV Record exists, FALSE if NV Record is empty */ static uint8 gapBondMgrUpdateCharCfg( uint8 idx, uint16 attrHandle, uint16 value ) { gapBondRec_t bondRec; // Space to read a Bond record from NV // Look for public address that is used (not all 0xFF's) if ( ( osal_snv_read( mainRecordNvID(idx), sizeof ( gapBondRec_t ), &bondRec ) == SUCCESS ) && ( osal_isbufset( bondRec.publicAddr, 0xFF, B_ADDR_LEN ) == FALSE ) ) { gapBondCharCfg_t charCfg[GAP_CHAR_CFG_MAX]; // Space to read a char cfg record from NV if ( osal_snv_read( gattCfgNvID(idx), sizeof ( charCfg ), charCfg ) == SUCCESS ) { uint8 update = FALSE; gapBondMgrInvertCharCfgItem( charCfg ); if ( attrHandle == GATT_INVALID_HANDLE ) { if ( osal_isbufset( (uint8 *)charCfg, 0x00, sizeof ( charCfg ) ) == FALSE ) { // Clear all characteristic configuration for this device VOID osal_memset( (void *)charCfg, 0x00, sizeof ( charCfg ) ); update = TRUE; } } else { gapBondCharCfg_t *pItem = gapBondMgrFindCharCfgItem( attrHandle, charCfg ); if ( pItem == NULL ) { // Must be a new item; ignore if the value is no operation (default) if ( ( value == GATT_CFG_NO_OPERATION ) || ( ( pItem = gapBondMgrFindCharCfgItem( GATT_INVALID_HANDLE, charCfg ) ) == NULL ) ) { return ( FALSE ); // No empty entry found } pItem->attrHandle = attrHandle; } if ( pItem->value != value ) { // Update characteristic configuration pItem->value = (uint8)value; if ( value == GATT_CFG_NO_OPERATION ) { // Erease the item pItem->attrHandle = GATT_INVALID_HANDLE; } update = TRUE; } } // Update the characteristic configuration of the bonded device. if ( update ) { gapBondMgrInvertCharCfgItem( charCfg ); VOID osal_snv_write( gattCfgNvID(idx), sizeof( charCfg ), charCfg ); } } return ( TRUE ); } return ( FALSE ); } /********************************************************************* * @fn gapBondMgrFindCharCfgItem * * @brief Find the Characteristic Configuration for a given attribute. * Uses the attribute handle to search the charactersitic * configuration table of a bonded device. * * @param attrHandle - attribute handle. * @param charCfgTbl - characteristic configuration table. * * @return pointer to the found item. NULL, otherwise. */ static gapBondCharCfg_t *gapBondMgrFindCharCfgItem( uint16 attrHandle, gapBondCharCfg_t *charCfgTbl ) { for ( uint8 i = 0; i < GAP_CHAR_CFG_MAX; i++ ) { if ( charCfgTbl[i].attrHandle == attrHandle ) { return ( &(charCfgTbl[i]) ); } } return ( (gapBondCharCfg_t *)NULL ); } /********************************************************************* * @fn gapBondMgrFindCharCfgItem * * @brief Invert the Characteristic Configuration for a given client. * * @param charCfgTbl - characteristic configuration table. * * @return none. */ static void gapBondMgrInvertCharCfgItem( gapBondCharCfg_t *charCfgTbl ) { for ( uint8 i = 0; i < GAP_CHAR_CFG_MAX; i++ ) { charCfgTbl[i].attrHandle = ~(charCfgTbl[i].attrHandle); charCfgTbl[i].value = ~(charCfgTbl[i].value); } } /********************************************************************* * @fn gapBondMgrAddBond * * @brief Save a bond from a GAP Auth Complete Event * * @param pBondRec - basic bond record * @param pLocalLTK - LTK used by this device during pairing * @param pDevLTK - LTK used by the connected device during pairing * @param pIRK - IRK used by the connected device during pairing * @param pSRK - SRK used by the connected device during pairing * @param signCounter - Sign counter used by the connected device during pairing * * @return TRUE, if done processing bond record. FALSE, otherwise. */ static uint8 gapBondMgrAddBond( gapBondRec_t *pBondRec, gapAuthCompleteEvent_t *pPkt ) { // See if this is a new bond record if ( pAuthEvt[pPkt->connectionHandle] == NULL ) { // Make sure we have bonding info if ( ( pBondRec == NULL ) || ( pPkt == NULL ) ) { return ( TRUE ); } // First see if we already have an existing bond for this device bondIdx = gapBondMgrFindAddr( pBondRec->publicAddr ); if ( bondIdx >= GAP_BONDINGS_MAX ) { bondIdx = gapBondMgrFindEmpty(); } #if(GAP_BOND_MGR_INDEX_REPLACE) /*replace bondIdx*/ if(bondIdx==GAP_BONDINGS_MAX) { bondIdx = (bondReplaceCnt++)%GAP_BONDINGS_MAX; BOND_LOG("replace BondIdx %d \n",bondIdx); } #endif BOND_LOG("BondMgrAdd idx=%03d\n",bondIdx); } if ( bondIdx < GAP_BONDINGS_MAX ) { // See if this is a new bond record if ( pAuthEvt[pPkt->connectionHandle] == NULL ) { gapBondCharCfg_t charCfg[GAP_CHAR_CFG_MAX]; // Save the main information osal_snv_write( mainRecordNvID(bondIdx), sizeof ( gapBondRec_t ), pBondRec ); // Write out FF's over the charactersitic configuration entry, to overwrite // any previous bond data that may have been stored VOID osal_memset( charCfg, 0xFF, sizeof ( charCfg ) ); VOID osal_snv_write( gattCfgNvID(bondIdx), sizeof ( charCfg ), charCfg ); // Update Bond RAM Shadow just with the newly added bond entry VOID osal_memcpy( &(bonds[bondIdx]), pBondRec, sizeof ( gapBondRec_t ) ); // Keep the OSAL message to store the security keys later - will be freed then pAuthEvt[pPkt->connectionHandle] = pPkt; } else { // If available, save the LTK information if ( pAuthEvt[pPkt->connectionHandle]->pSecurityInfo ) { VOID osal_snv_write( localLTKNvID(bondIdx), sizeof ( gapBondLTK_t ), pAuthEvt[pPkt->connectionHandle]->pSecurityInfo ); pAuthEvt[pPkt->connectionHandle]->pSecurityInfo = NULL; } // If availabe, save the connected device's LTK information if ( pAuthEvt[pPkt->connectionHandle]->pDevSecInfo ) { VOID osal_snv_write( devLTKNvID(bondIdx), sizeof ( gapBondLTK_t ), pAuthEvt[pPkt->connectionHandle]->pDevSecInfo ); pAuthEvt[pPkt->connectionHandle]->pDevSecInfo = NULL; } // If available, save the connected device's IRK if ( pAuthEvt[pPkt->connectionHandle]->pIdentityInfo ) { VOID osal_snv_write( devIRKNvID(bondIdx), KEYLEN, pAuthEvt[pPkt->connectionHandle]->pIdentityInfo->irk ); pAuthEvt[pPkt->connectionHandle]->pIdentityInfo = NULL; } // If available, save the connected device's Signature information if ( pAuthEvt[pPkt->connectionHandle]->pSigningInfo ) { VOID osal_snv_write( devCSRKNvID(bondIdx), KEYLEN, pAuthEvt[pPkt->connectionHandle]->pSigningInfo->srk ); VOID osal_snv_write( devSignCounterNvID(bondIdx), sizeof ( uint32 ), &(pAuthEvt[pPkt->connectionHandle]->pSigningInfo->signCounter) ); pAuthEvt[pPkt->connectionHandle]->pSigningInfo = NULL; } // else { if ( autoSyncWhiteList ) { gapBondMgr_SyncWhiteList(); } // Update the GAP Privacy Flag Properties gapBondSetupPrivFlag(); return ( TRUE ); } } // We have more info to store return ( FALSE ); } return ( TRUE ); } /********************************************************************* * @fn gapBondMgrGetStateFlags * * @brief Gets the state flags field of a bond record in NV * * @param idx * * @return stateFlags field */ static uint8 gapBondMgrGetStateFlags( uint8 idx ) { gapBondRec_t bondRec; if ( osal_snv_read( mainRecordNvID(idx), sizeof ( gapBondRec_t ), &bondRec ) == SUCCESS ) { return ( bondRec.stateFlags ); } return ( 0 ); } /********************************************************************* * @fn gapBondMgrGetPublicAddr * * @brief Copy the public Address from a bonding record * * @param idx - Bond record index * @param pAddr - a place to put the public address from NV * * @return SUCCESS if successful. * Otherwise failure. */ static bStatus_t gapBondMgrGetPublicAddr( uint8 idx, uint8 *pAddr ) { bStatus_t stat; // return value gapBondRec_t bondRec; // Work space for main bond record // Check parameters if ( (idx >= GAP_BONDINGS_MAX) || (pAddr == NULL) ) { return ( INVALIDPARAMETER ); } stat = osal_snv_read( mainRecordNvID(idx), sizeof ( gapBondRec_t ), &bondRec ); if ( stat == SUCCESS ) { VOID osal_memcpy( pAddr, bondRec.publicAddr, B_ADDR_LEN ); } return ( stat ); } /********************************************************************* * @fn gapBondMgrFindReconnectAddr * * @brief Look through the bonding entries to find a * reconnection address. * * @param pReconnectAddr - device address to look for * * @return index to found bonding (0 - (GAP_BONDINGS_MAX-1), * GAP_BONDINGS_MAX if no empty entries */ static uint8 gapBondMgrFindReconnectAddr( uint8 *pReconnectAddr ) { // Item doesn't exist, so create all the items for ( uint8 idx = 0; idx < GAP_BONDINGS_MAX; idx++ ) { // compare reconnection address if ( osal_memcmp( bonds[idx].reconnectAddr, pReconnectAddr, B_ADDR_LEN ) ) { return ( idx ); // Found it } } return ( GAP_BONDINGS_MAX ); } /********************************************************************* * @fn gapBondMgrFindAddr * * @brief Look through the bonding entries to find an address. * * @param pDevAddr - device address to look for * * @return index to empty bonding (0 - (GAP_BONDINGS_MAX-1), * GAP_BONDINGS_MAX if no empty entries */ static uint8 gapBondMgrFindAddr( uint8 *pDevAddr ) { // Item doesn't exist, so create all the items for ( uint8 idx = 0; idx < GAP_BONDINGS_MAX; idx++ ) { // Read in NV Main Bond Record and compare public address if ( osal_memcmp( bonds[idx].publicAddr, pDevAddr, B_ADDR_LEN ) ) { return ( idx ); // Found it } } return ( GAP_BONDINGS_MAX ); } /********************************************************************* * @fn gapBondMgrResolvePrivateAddr * * @brief Look through the NV bonding entries to resolve a private * address. * * @param pDevAddr - device address to look for * * @return index to found bonding (0 - (GAP_BONDINGS_MAX-1), * GAP_BONDINGS_MAX if no entry found */ static uint8 gapBondMgrResolvePrivateAddr( uint8 *pDevAddr ) { for ( uint8 idx = 0; idx < GAP_BONDINGS_MAX; idx++ ) { uint8 IRK[KEYLEN]; // Read in NV IRK Record and compare resovable address if ( osal_snv_read( devIRKNvID(idx), KEYLEN, IRK ) == SUCCESS ) { if ( ( osal_isbufset( IRK, 0xFF, KEYLEN ) == FALSE ) && ( GAP_ResolvePrivateAddr( IRK, pDevAddr ) == SUCCESS ) ) { return ( idx ); // Found it } } } return ( GAP_BONDINGS_MAX ); } /********************************************************************* * @fn gapBondMgrReadBonds * * @brief Read through NV and store them in RAM. * * @param none * * @return none */ static void gapBondMgrReadBonds( void ) { for ( uint8 idx = 0; idx < GAP_BONDINGS_MAX; idx++ ) { // See if the entry exists in NV if ( osal_snv_read( mainRecordNvID(idx), sizeof( gapBondRec_t ), &(bonds[idx]) ) != SUCCESS ) { // Can't read the entry, assume that it doesn't exist VOID osal_memset( bonds[idx].publicAddr, 0xFF, B_ADDR_LEN ); VOID osal_memset( bonds[idx].reconnectAddr, 0xFF, B_ADDR_LEN ); bonds[idx].stateFlags = 0; } } if ( autoSyncWhiteList ) { gapBondMgr_SyncWhiteList(); } // Update the GAP Privacy Flag Properties gapBondSetupPrivFlag(); } /********************************************************************* * @fn gapBondMgrFindEmpty * * @brief Look through the bonding NV entries to find an empty. * * @param none * * @return index to empty bonding (0 - (GAP_BONDINGS_MAX-1), * GAP_BONDINGS_MAX if no empty entries */ static uint8 gapBondMgrFindEmpty( void ) { // Item doesn't exist, so create all the items for ( uint8 idx = 0; idx < GAP_BONDINGS_MAX; idx++ ) { // Look for public address of all 0xFF's if ( osal_isbufset( bonds[idx].publicAddr, 0xFF, B_ADDR_LEN ) ) { return ( idx ); // Found one } } return ( GAP_BONDINGS_MAX ); } /********************************************************************* * @fn gapBondMgrBondTotal * * @brief Look through the bonding NV entries calculate the number * entries. * * @param none * * @return total number of bonds found */ static uint8 gapBondMgrBondTotal( void ) { uint8 numBonds = 0; // Item doesn't exist, so create all the items for ( uint8 idx = 0; idx < GAP_BONDINGS_MAX; idx++ ) { // Look for public address that are not 0xFF's if ( osal_isbufset( bonds[idx].publicAddr, 0xFF, B_ADDR_LEN ) == FALSE ) { numBonds++; // Found one } } return ( numBonds ); } /********************************************************************* * @fn gapBondMgrEraseAllBondings * * @brief Write all 0xFF's to all of the bonding entries * * @param none * * @return SUCCESS if successful. * Otherwise, NV_OPER_FAILED for failure. */ static bStatus_t gapBondMgrEraseAllBondings( void ) { bStatus_t stat = SUCCESS; // return value // Item doesn't exist, so create all the items for ( uint8 idx = 0; (idx < GAP_BONDINGS_MAX) && (stat == SUCCESS); idx++ ) { // Erasing will write/create a bonding entry stat = gapBondMgrEraseBonding( idx ); } return ( stat ); } /********************************************************************* * @fn gapBondMgrEraseBonding * * @brief Write all 0xFF's to the complete bonding record * * @param idx - bonding index * * @return SUCCESS if successful. * Otherwise, NV_OPER_FAILED for failure. */ static bStatus_t gapBondMgrEraseBonding( uint8 idx ) { bStatus_t ret; gapBondRec_t bondRec; if ( idx == bondIdx ) { // Stop ongoing bond store process to prevent any invalid data be written. // osal_clear_event( gapBondMgr_TaskID, GAP_BOND_SYNC_CC_EVT ); // osal_clear_event( gapBondMgr_TaskID, GAP_BOND_SAVE_REC_EVT ); // gapBondFreeAuthEvt(); } // First see if bonding record exists in NV, then write all 0xFF's to it if ( ( osal_snv_read( mainRecordNvID(idx), sizeof ( gapBondRec_t ), &bondRec ) == SUCCESS ) && (osal_isbufset( bondRec.publicAddr, 0xFF, B_ADDR_LEN ) == FALSE) ) { gapBondLTK_t ltk; gapBondCharCfg_t charCfg[GAP_CHAR_CFG_MAX]; VOID osal_memset( &bondRec, 0xFF, sizeof ( gapBondRec_t ) ); VOID osal_memset( <k, 0xFF, sizeof ( gapBondLTK_t ) ); VOID osal_memset( charCfg, 0xFF, sizeof ( charCfg ) ); // Write out FF's over the entire bond entry. ret = osal_snv_write( mainRecordNvID(idx), sizeof ( gapBondRec_t ), &bondRec ); ret |= osal_snv_write( localLTKNvID(idx), sizeof ( gapBondLTK_t ), <k ); ret |= osal_snv_write( devLTKNvID(idx), sizeof ( gapBondLTK_t ), <k ); ret |= osal_snv_write( devIRKNvID(idx), KEYLEN, ltk.LTK ); ret |= osal_snv_write( devCSRKNvID(idx), KEYLEN, ltk.LTK ); ret |= osal_snv_write( devSignCounterNvID(idx), sizeof ( uint32 ), ltk.LTK ); // Write out FF's over the charactersitic configuration entry. ret |= osal_snv_write( gattCfgNvID(idx), sizeof ( charCfg ), charCfg ); } else { ret = SUCCESS; } return ( ret ); } /********************************************************************* * @brief Task Initialization function. * * Internal function defined in gapbondmgr.h. */ void GAPBondMgr_Init( uint8 task_id ) { gapBondMgr_TaskID = task_id; // Save task ID // Setup Bond RAM Shadow gapBondMgrReadBonds(); } /********************************************************************* * @brief Task Event Processor function. * * Internal function defined in gapbondmgr.h. */ uint16 GAPBondMgr_ProcessEvent( uint8 task_id, uint16 events ) { VOID task_id; // OSAL required parameter that isn't used in this function if ( events & SYS_EVENT_MSG ) { uint8 *pMsg; if ( (pMsg = osal_msg_receive( gapBondMgr_TaskID )) != NULL ) { if ( gapBondMgr_ProcessOSALMsg( (osal_event_hdr_t *)pMsg ) ) { // Release the OSAL message VOID osal_msg_deallocate( pMsg ); } } // return unprocessed events return (events ^ SYS_EVENT_MSG); } // if ( events & GAP_BOND_SAVE_REC_EVT ) // { // // Save bonding record in NV // if ( gapBondMgrAddBond( NULL, NULL ) ) // { // // Notify our task to update NV with CCC values stored in GATT database // osal_set_event( gapBondMgr_TaskID, GAP_BOND_SYNC_CC_EVT ); // return (events ^ GAP_BOND_SAVE_REC_EVT); // } // return ( GAP_BOND_SAVE_REC_EVT ); // } // if ( events & GAP_BOND_SYNC_CC_EVT ) // { // // Update NV to have same CCC values as GATT database // // Note: pAuthEvt is a global variable used for deferring the storage // if ( gapBondMgr_SyncCharCfg( pAuthEvt->connectionHandle ) ) // { // if ( pGapBondCB && pGapBondCB->pairStateCB ) // { // // Assume SUCCESS since we got this far. // pGapBondCB->pairStateCB( pAuthEvt->connectionHandle, GAPBOND_PAIRING_STATE_COMPLETE, SUCCESS ); // } // // We're done storing bond record and CCC values in NV // gapBondFreeAuthEvt(); // return (events ^ GAP_BOND_SYNC_CC_EVT); // } // return ( GAP_BOND_SYNC_CC_EVT ); // } // Discard unknown events return 0; } /********************************************************************* * @fn gapBondMgr_ProcessOSALMsg * * @brief Process an incoming task message. * * @param pMsg - message to process * * @return TRUE if safe to deallocate incoming message, FALSE otherwise. */ static uint8 gapBondMgr_ProcessOSALMsg( osal_event_hdr_t *pMsg ) { uint8 safeToDealloc = TRUE; switch ( pMsg->event ) { case GAP_MSG_EVENT: safeToDealloc = GAPBondMgr_ProcessGAPMsg( (gapEventHdr_t *)pMsg ); break; case GATT_MSG_EVENT: gapBondMgr_ProcessGATTMsg( (gattMsgEvent_t *)pMsg ); break; case GATT_SERV_MSG_EVENT: gapBondMgr_ProcessGATTServMsg( (gattEventHdr_t *)pMsg ); break; default: break; } return ( safeToDealloc ); } /********************************************************************* * @fn GAPBondMgr_CheckNVLen * * @brief This function will check the length of an NV Item. * * @param id - NV ID. * @param len - lengths in bytes of item. * * @return SUCCESS or FAILURE */ uint8 GAPBondMgr_CheckNVLen( uint8 id, uint8 len ) { uint8 stat = FAILURE; // Convert to index switch ( (id - BLE_NVID_GAP_BOND_START) % GAP_BOND_REC_IDS ) { case GAP_BOND_REC_ID_OFFSET: if ( len == sizeof ( gapBondRec_t ) ) { stat = SUCCESS; } break; case GAP_BOND_LOCAL_LTK_OFFSET: case GAP_BOND_DEV_LTK_OFFSET: if ( len == sizeof ( gapBondLTK_t ) ) { stat = SUCCESS; } break; case GAP_BOND_DEV_IRK_OFFSET: case GAP_BOND_DEV_CSRK_OFFSET: if ( len == KEYLEN ) { stat = SUCCESS; } break; case GAP_BOND_DEV_SIGN_COUNTER_OFFSET: if ( len == sizeof ( uint32 ) ) { stat = SUCCESS; } break; default: break; } return ( stat ); } /********************************************************************* * @fn gapBondMgr_ProcessGATTMsg * * @brief Process an incoming GATT message. * * @param pMsg - pointer to received message * * @return none */ static void gapBondMgr_ProcessGATTMsg( gattMsgEvent_t *pMsg ) { // Process the GATT message switch ( pMsg->method ) { case ATT_HANDLE_VALUE_CFM: // Clear Service Changed flag for this client VOID GAPBondMgr_ServiceChangeInd( pMsg->connHandle, 0x00 ); break; default: // Unknown message break; } } /********************************************************************* * @fn gapBondMgr_ProcessGATTServMsg * * @brief Process an incoming GATT Server App message. * * @param pMsg - pointer to received message * * @return none */ static void gapBondMgr_ProcessGATTServMsg( gattEventHdr_t *pMsg ) { // Process the GATT Server App message switch ( pMsg->method ) { case GATT_CLIENT_CHAR_CFG_UPDATED_EVENT: { gattClientCharCfgUpdatedEvent_t *pEvent = (gattClientCharCfgUpdatedEvent_t *)pMsg; VOID GAPBondMgr_UpdateCharCfg( pEvent->connHandle, pEvent->attrHandle, pEvent->value ); } break; default: // Unknown message break; } } /********************************************************************* * @fn gapBondMgrSendServiceChange * * @brief Tell the GATT that a service change is needed. * * @param pLinkItem - pointer to connection information * * @return none */ static void gapBondMgrSendServiceChange( linkDBItem_t *pLinkItem ) { VOID GATTServApp_SendServiceChangedInd( pLinkItem->connectionHandle, gapBondMgr_TaskID ); } /********************************************************************* * @fn gapBondSetupPrivFlag * * @brief Setup the GAP Privacy Flag properties. * * @param none * * @return none */ static void gapBondSetupPrivFlag( void ) { uint8 privFlagProp; if ( gapBondMgrBondTotal() > 1 ) { privFlagProp = GATT_PROP_READ; } else { privFlagProp = GATT_PROP_READ | GATT_PROP_WRITE; } // Setup the VOID GGS_SetParameter( GGS_PERI_PRIVACY_FLAG_PROPS, sizeof ( uint8 ), &privFlagProp ); } /********************************************************************* * @fn gapBondMgrAuthenticate * * @brief Initiate authentication * * @param connHandle - connection handle * @param addrType - peer address type * @param pPairReq - Enter these parameters if the Pairing Request was already received. * NULL, if waiting for Pairing Request or if initiating. * * @return none */ static void gapBondMgrAuthenticate( uint16 connHandle, uint8 addrType, gapPairingReq_t *pPairReq ) { gapAuthParams_t params; VOID osal_memset( ¶ms, 0, sizeof ( gapAuthParams_t ) ); // Setup the pairing parameters params.connectionHandle = connHandle; params.secReqs.ioCaps = gapBond_IOCap; params.secReqs.oobAvailable = gapBond_OOBDataFlag; params.secReqs.maxEncKeySize = gapBond_KeySize; params.secReqs.keyDist.sEncKey = (gapBond_KeyDistList & GAPBOND_KEYDIST_SENCKEY) ? TRUE : FALSE; params.secReqs.keyDist.sIdKey = (gapBond_KeyDistList & GAPBOND_KEYDIST_SIDKEY) ? TRUE : FALSE; params.secReqs.keyDist.mEncKey = (gapBond_KeyDistList & GAPBOND_KEYDIST_MENCKEY) ? TRUE : FALSE; params.secReqs.keyDist.mIdKey = (gapBond_KeyDistList & GAPBOND_KEYDIST_MIDKEY) ? TRUE : FALSE; params.secReqs.keyDist.mSign = (gapBond_KeyDistList & GAPBOND_KEYDIST_MSIGN) ? TRUE : FALSE; params.secReqs.keyDist.sSign = (gapBond_KeyDistList & GAPBOND_KEYDIST_SSIGN) ? TRUE : FALSE; // Is bond manager setup for OOB data? if ( gapBond_OOBDataFlag ) { VOID osal_memcpy( params.secReqs.oob, gapBond_OOBData, KEYLEN ); } if ( gapBond_Bonding && addrType != ADDRTYPE_PUBLIC ) { // Force a slave ID key params.secReqs.keyDist.sIdKey = TRUE; } params.secReqs.authReq |= (gapBond_Bonding) ? SM_AUTH_STATE_BONDING : 0; params.secReqs.authReq |= (gapBond_MITM) ? SM_AUTH_STATE_AUTHENTICATED : 0; params.secReqs.authReq |= (gapBond_SC) ? SM_AUTH_STATE_SC : 0; params.secReqs.authReq |= (gapBond_keyPress) ? SM_AUTH_STATE_KEYPRESS : 0; params.secReqs.authReq |= (gapBond_CT2) ? SM_AUTH_STATE_CT2 : 0; GAP_Authenticate( ¶ms, pPairReq ); } #if ( HOST_CONFIG & PERIPHERAL_CFG ) /********************************************************************* * @fn gapBondMgrSlaveSecurityReq * * @brief Send a slave security request * * @param connHandle - connection handle * * @return none */ static void gapBondMgrSlaveSecurityReq( uint16 connHandle ) { uint8 authReq = 0; authReq |= (gapBond_Bonding) ? SM_AUTH_STATE_BONDING : 0; authReq |= (gapBond_MITM) ? SM_AUTH_STATE_AUTHENTICATED : 0; authReq |= (gapBond_SC) ? SM_AUTH_STATE_SC : 0; authReq |= (gapBond_keyPress) ? SM_AUTH_STATE_KEYPRESS : 0; authReq |= (gapBond_CT2) ? SM_AUTH_STATE_CT2 : 0; GAP_SendSlaveSecurityRequest( connHandle, authReq ); } #endif /********************************************************************* * @fn gapBondMgrBondReq * * @brief Initiate a GAP bond request * * @param connHandle - connection handle * @param idx - NV index of bond entry * @param stateFlags - bond state flags * @param role - master or slave role * @param startEncryption - whether or not to start encryption * * @return none */ static void gapBondMgrBondReq( uint16 connHandle, uint8 idx, uint8 stateFlags, uint8 role, uint8 startEncryption ) { smSecurityInfo_t ltk; osalSnvId_t nvId; if ( role == GAP_PROFILE_CENTRAL ) { nvId = devLTKNvID( idx ); } else { nvId = localLTKNvID( idx ); } // Initialize the NV structures VOID osal_memset( <k, 0, sizeof ( smSecurityInfo_t ) ); if ( osal_snv_read( nvId, sizeof ( smSecurityInfo_t ), <k ) == SUCCESS ) { if ( (ltk.keySize >= MIN_ENC_KEYSIZE) && (ltk.keySize <= MAX_ENC_KEYSIZE) ) { bStatus_t ret = GAP_Bond( connHandle, ((stateFlags & GAP_BONDED_STATE_AUTHENTICATED) ? TRUE : FALSE), <k, startEncryption ); } } } /********************************************************************* * @fn gapBondMgr_SyncWhiteList * * @brief syncronize the White List with the bonds * * @param none * * @return none */ static void gapBondMgr_SyncWhiteList( void ) { //erase the White List VOID HCI_LE_ClearWhiteListCmd(); // Write bond addresses into the White List for( uint8 i = 0; i < GAP_BONDINGS_MAX; i++) { // Make sure empty addresses are not added to the White List if ( osal_isbufset( bonds[i].publicAddr, 0xFF, B_ADDR_LEN ) == FALSE ) { VOID HCI_LE_AddWhiteListCmd( HCI_PUBLIC_DEVICE_ADDRESS, bonds[i].publicAddr ); } } } /********************************************************************* * @fn gapBondMgr_SyncCharCfg * * @brief Update the Bond Manager to have the same configurations as * the GATT database. * * @param connHandle - the current connection handle to find client configurations for * * @return TRUE if sync done. FALSE, otherwise. */ static uint8 gapBondMgr_SyncCharCfg( uint16 connHandle ) { static gattAttribute_t *pAttr = NULL; static uint16 service; // Only attributes with attribute handles between and including the Starting // Handle parameter and the Ending Handle parameter that match the requested // attribute type and the attribute value will be returned. // All attribute types are effectively compared as 128-bit UUIDs, // even if a 16-bit UUID is provided in this request or defined // for an attribute. if ( pAttr == NULL ) { pAttr = GATT_FindHandleUUID( GATT_MIN_HANDLE, GATT_MAX_HANDLE, clientCharCfgUUID, ATT_BT_UUID_SIZE, &service ); } while ( pAttr != NULL ) { uint16 len; uint8 attrVal[ATT_BT_UUID_SIZE]; // It is not possible to use this request on an attribute that has a value // that is longer than 2. if ( GATTServApp_ReadAttr( connHandle, pAttr, service, attrVal, &len, 0, ATT_BT_UUID_SIZE ) == SUCCESS ) { uint16 value = BUILD_UINT16(attrVal[0], attrVal[1]); if ( value != GATT_CFG_NO_OPERATION ) { // NV must be updated to meet configuration of the database VOID GAPBondMgr_UpdateCharCfg( connHandle, pAttr->handle, value ); } } // Try to find the next attribute pAttr = GATT_FindNextAttr( pAttr, GATT_MAX_HANDLE, service, NULL ); } return ( pAttr == NULL ); } /********************************************************************* * @fn gapBondFreeAuthEvt * * @brief Free GAP Authentication Complete event. * * @param none * * @return none */ static void gapBondFreeAuthEvt( uint16 connHandle ) { if ( pAuthEvt[connHandle] != NULL ) { // Release the OSAL message VOID osal_msg_deallocate( (uint8 *)pAuthEvt[connHandle] ); pAuthEvt[connHandle] = NULL; } bondIdx = GAP_BONDINGS_MAX; } #endif // ( CENTRAL_CFG | PERIPHERAL_CFG ) /********************************************************************* *********************************************************************/