CH57x/CH58x/CH59xBLE利用IRK解析RPA : 识别可解析私有地址对应的身份地址
前言
在 BLE 开发中,如果希望主机一连接就能判断它是否已经绑定过。直接比对连接事件里的六字节地址,在部分电脑上看似可行,换成手机后却发现地址会变化,同一部手机经常被判成新设备,这类地址通常可以看到它的地址类型为0x03,也就是可解析私有地址;
常见的地址类型如下:
|
类型 |
含义 |
识别方式 |
|
0x00 PUBLIC |
公开地址 |
按类型和地址匹配 |
|
0x01 STATIC |
随机静态地址 |
按类型和地址匹配 |
|
0x02 PRIVATE_NONRESOLVE |
不可解析私有地址 |
不能用 IRK 解析 |
|
0x03 PRIVATE_RESOLVE |
可解析私有地址 |
用对端 IRK 匹配 |
本文只讨论这个识别判断:当设备已经保存了手机的绑定信息(包含1字节地址类型和6字节可解析私有地址)和对端 IRK 时,可用当前 RPA 先匹配绑定记录,再读取该记录的身份地址。
解析地址的含义
可解析私有地址 RPA 是会变化的连接地址。IRK 是身份解析密钥,用于判断某个 RPA 是否属于持有该身份的设备。

解析接口的结果是匹配或不匹配,不会把 RPA 原地改写成手机的真实 MAC。
准确流程是:当前 RPA → 逐一尝试有效绑定记录的对端 IRK → 找到匹配槽位 → 读取该槽位的身份地址。身份地址可以是公开地址,也可以是随机静态地址;
解析与读取步骤
1、BLE_SNV_NUM决定着从机可同时存有多少个主机的绑定信息
【SNV】
BLE_SNV - 是否开启SNV功能,用于储存绑定信息( 默认:TRUE )
BLE_SNV_ADDR - SNV信息保存地址,使用data flash最后512字节( 默认:0x77E00 )
BLE_SNV_BLOCK - SNV信息保存块大小( 默认:256 )
BLE_SNV_NUM - SNV信息保存数量( 默认:1 )
2、通过协议栈给出的tmos_snv_read接口和读取偏移量,我们可得到对端IRK所存放的位置;

本例通过 tmos_snv_read 访问协议栈的逻辑 SNV 项;对端 IRK 的项 ID 由 devIRKNvID(bondIdx) 计算,主绑定记录由 mainRecordNvID(bondIdx) 定位;
#define BLE_NVID_GAP_BOND_START 0x0200
#define GAP_BOND_REC_IDS 6
#define GAP_BOND_DEV_IRK_OFFSET 3
/* 本例头文件宏的等价表达式,仅用于解释 */
/* devIRKNvID(n) = 0x0200 + n * 6 + 3 */
以BLE_SNV_NUM = 3为例,
|
协议栈槽位 bondIdx |
主记录 SNV ID |
对端 IRK SNV ID |
|
0 |
0x0200 |
0x0203 |
|
1 |
0x0206 |
0x0209 |
|
2 |
0x020C |
0x020F |
bondIdx 传 0、1、2 等槽位号,不要先计算出 0x0203 再传给 devIRKNvID,否则宏会再次计算,最终读到错误位置,应用自己维护的设备列表下标也不等于协议栈绑定槽位.
uint8_t peerIRK[KEYLEN];
gapBondRec_t record = {0};
/* 读取指定槽位的对端 IRK */
tmos_snv_read(devIRKNvID(bondIdx),
sizeof peerIRK, peerIRK);
/* 读取该槽位的身份信息 */
tmos_snv_read(mainRecordNvID(bondIdx),
sizeof record, &record);
/* record.publicAddrType:身份地址类型 */
/* record.publicAddr:身份地址 */
BLE_NVID_IRK 和 GAPROLE_IRK 对应本设备自身的 IRK,不能拿来识别手机,这里需要的是 devIRKNvID 对应的对端 IRK
3、最终实现
在我们配对完成至多BLE_SNV_NUM个主机后,如果下次连接需要判断本次连接的主机是否在已配对列表中,可参考以下函数实现:
函数作用:
①传入pType=0x03,和待解析pAddr,如果协议栈存储列表中存有对应设备信息,可通过dstType和dstAddr接取身份地址及类型,返回0;
②传入pType=0x03,和待解析pAddr,如果协议栈中未匹配到设备信息,不处理,原封返回至dstType和dstAddr,返回1;
③传入pType=0x00/0x01/0x02,不处理,原封返回至dstType和dstAddr,返回0;
/*
pType-连接事件传入原始地址类型
pAddr-连接事件传入原始地址
dstType-待接收的真实地址类型
dstAddr-待接收的真实身份地址
*/
uint8_t ble_ResolveAddr(uint8_t pType, uint8_t *pAddr,uint8_t *dstType,uint8_t *dstAddr)
{
uint8_t matchedBondIdx = 0xFF;
if(pType == ADDRTYPE_PRIVATE_RESOLVE)//如果地址是可解析私有地址(地址会动态变化)
{
//利用主机IRK逐一匹配身份地址
uint8_t bondIdx = 0;
for(bondIdx = 0; bondIdx < BLE_SNV_NUM; bondIdx++)
{
uint8_t peerIRK[KEYLEN];
uint8_t addr[B_ADDR_LEN];
gapBondRec_t record = {0};
//读取当前槽位的对端 IRK
uint8_t status = tmos_snv_read(devIRKNvID(bondIdx),sizeof(peerIRK),peerIRK);
PRINT("bondIdx = %d, IRK read status = %02X\n",bondIdx,status);
if(status != SUCCESS)//
{
PRINT("IRK READ FAIL\n");
continue;//读不到则跳过
}
//验证当前 RPA(ResolvePrivateAddr) 是否匹配该 IRK
status = GAP_ResolvePrivateAddr(peerIRK, pAddr);
PRINT("bondIdx = %d, resolve status = %02X\n",bondIdx,status);
if(status != SUCCESS)
{
continue;//不匹配则跳过当前流程
}
matchedBondIdx = bondIdx;
//匹配成功后读取该槽位的真实身份地址
status = tmos_snv_read( mainRecordNvID(bondIdx),sizeof(record), &record );
if(status == SUCCESS)
{
PRINT("Matched bondIdx: %d\n",bondIdx);
PRINT("Identity type: %02X\n",record.publicAddrType);
PRINT("Identity address: ");
for(uint8_t i=0;i<6;i++)
{
PRINT("%02X ",record.publicAddr[i]);
}
PRINT("\n");
*dstType = record.publicAddrType;
tmos_memcpy(dstAddr,record.publicAddr,6);
}
else
{
PRINT("Read identity failed: %02X\n",status);
}
break;
}
if(bondIdx >= (BLE_SNV_NUM))
{
*dstType = pType;
tmos_memcpy(dstAddr,pAddr,6);
PRINT("NEW PAIR DEVICE\n");
//所有轮次未找到,说明是新配对的ADDRTYPE_PRIVATE_RESOLVE类型主机,
//再进入配对完成GAPBOND_PAIRING_STATE_COMPLETE回调后需要对原始地址解析为真实身份地址再存储到flash
return 1;
}
}
else //其余主机型号基本都是0x00,拿到的都是公共地址,无需解析,0x01/0x02本文未作处理
{
*dstType = pType;
tmos_memcpy(dstAddr,pAddr,6);
}
return 0;
}
调用位置参考:


浙公网安备 33010602011771号