沁恒微 Ble 蓝牙配对绑定说明测试
Ble 配对绑定的说明与测试 ...... 矜辰所致
前言
我们在使用 Ble 蓝牙的时候,有些设备需要数据加密,防止陌生设备读写数据,我们就需要进行配对绑定操作。但什么是配对,什么是绑定,和之前我们普通的连接通信有什么区别?实际应用中我们如何设置配对绑定?
本文我们就来说明一下 Ble 配对绑定的基础知识以及在 沁恒微 Ble 上配对绑定的代码实现。
相关博文:CH58x/CH59x 系列蓝牙芯片从机示例解析沁恒微蓝牙 GATT 应用框架说明沁恒 RISC-V 蓝牙芯片 Flash 分区管理及操作
我是矜辰所致,全网同名,尽量用心写好每一系列文章,不浮夸,不将就,认真对待学知识的我们,矜辰所致,金石为开!
📑 本文目录前言一、🎯基础介绍├─ 1.1 什么是配对绑定├─ 1.2 为什么需要配对绑定├─ 1.3 什么决定需不需要配对绑定├─ 1.4 配对流程├─ 1.5 细节补充说明二、🔍 EVT 示例说明├─ 2.1 与配对有关的代码说明├─ 2.2 示例修改测试│ ├─ 2.2.1 增加配对相关回调函数│ ├─ 2.2.2 关于回连│ ├─ 2.2.3 主机示例配对绑定测试├─ 2.3 其他说明结语
一、 基础介绍
1.1 什么是配对绑定
先简单介绍一下什么是配对,什么是绑定?
我们用简短的术语表达:
-
• 配对两个设备当场交换密钥,建立加密连接 (一次性)。 -
• 绑定两个设备记住密钥,下次自动加密连接 (永久)。
我们之前很多基本的示例测试,数据传输,都是没有经过配对绑定的明文,可以通过蓝牙分析仪获取控制通讯的数据包,可以直接解析出来数据内容。
经过配对绑定之后,我们抓取到的数据包就是经过加密的报文,无法直接解析。
对于连接,配对,绑定的关联:
-
• 配对一定要先连接,没有连接,就没法配对; -
• 绑定一定要先配对,没有配对,就没法绑定。
1.2 为什么需要配对绑定
原因概括来说就两点:
-
1. 安全性没有配对绑定的空中报文是明文,能够被第三方抓包(比比如一般测试情况下蓝牙分析仪抓包),很多场合下为了保证两个设备通信的安全性,必须防窃听、防篡改、防冒充,所以需要配对绑定。 -
2. 便捷性绑定过后,设备自动回连,不需要再次手动点击配对,自动进行加密回连,安全便捷。
1.3 什么决定需不需要配对绑定
需不需要配对绑定由从机的特征值权限决定!
从机的特征值权限,我们以前在学习 GATT 应用框架时的博文《 沁恒微蓝牙 GATT 应用框架说明 》有过说明,如下图:

具备上述属性的特征值必须要配对绑定才能进行正常的数据交互。
但是对于普通属性的特征值,不需要配对绑定就能正常读写的特征值,我们主动进行配对绑定,也是没有问题的,配对过后就变成加密通讯 ,对于普通属性的特征值配不配对都不影响通信。
1.4 配对流程
说明:只要初始化的时候设定好了配对绑定参数,配对流程是协议栈自动进行的,对于应用来说,本小节简单了解即可,不影响使用。
配对开始都是由”主机开始的“,这里指的是 启动配对 肯定是由主机发起,但是从机可以发 Security Request 请求配对,使得主机发起配对流程。
“谁输入密码”由双方 IO 能力协商决定——可能是主机、从机、双方确认,或无密码。
配对流程可以概括如下:
主机发起 → 特性交换(IO / 认证方式)→ 认证执行(显示 / 输入 / 确认)→ 密钥生成 → 链路加密 → 可选绑定保存。
-
1. 主机发起主机发 Pairing Request 开始配对; -
2. 特性交换(IO / 认证方式)主机 ↔ 从机互相告诉对方 3 件事:①、IO 能力:有没有屏幕,有没有键盘 / 输入,决定后面谁输密码、谁显示密码。②、MITM:中间人攻击,MITM = TRUE 时要求防中间人,必须输密码、或数字比对,不能随便就连。MITM = FALSE 时可以用 Just Works 直接配对。
当从设备的 IO 能力 = NO_INPUT_NO_OUTPUT(无输入无输出),即使 MITM = TRUE 也只能走 Just Works 免密配对③、是否绑定(Bonding)
-
3. 认证执行(显示 / 输入 / 确认)根据第二部谈好的规则,走一种配对方式:①、Just Works:无密码、无显示、无输入,直接配对②、Passkey Entry:一方显示 6 位密码,另一方输入③、Numeric Comparison:两边都显示一样的 6 位密码/两边都点 “确认” -
4. 密钥生成 → 生成 STKSTK : Short Term Key 短期密钥,只对本次连接有效,用来给当前链路做 AES 加密 。 -
5. 链路加密双方用 STK 启动加密,所有数据变成 AES 密文 -
6. 可选绑定保存如果第二步里 bonding = TRUE:就需要分发长期密钥 LTK + RAND + EDIV + IRK(可选)+ CSRK(可选,极少用),保存到 Flash。对于上述几种长期密钥,说明见下面表格:
|
|
|
|
|
| LTK |
|
|
|
| EDIV |
|
|
|
| Rand |
|
|
|
| IRK |
|
|
|
| CSRK |
|
|
|
1.5 细节补充说明
-
• 蓝牙两个设备建立连接的过程(广从机广播、手机扫描、手机发起连接、建立物理连接成功)是明文,可以通过抓包工具获取通讯包。加密在配对后才启用! -
• 配对绑定不阻止别人连接,但阻止别人访问加密特征值;明文特征值仍可读写,除非配对模式设为拒绝新连接。 -
• 从机多连接:从机同时跟多个主机建立物理连接,但每个主机能不能读写、能读哪些,由 特征值权限 + 绑定状态 单独控制。从机可以绑定多个主机,每个绑定都是独立密钥;从机 Flash 里存一个绑定列表,可以存多组 LTK 密钥,对应多个手机。绑定多个后,所有已绑定手机都能自动回连、加密通信。绑定信息需要存放到 Flash ,需要占用内存空间,沁恒微EVT 里面决定绑定数量的在 CONFIG.h里面的宏定义BLE_SNV_NUM。
二、 EVT 示例说明
本文以 CH58x EVT 为示例说明,CH58x CH59x 系列芯片大体一致,具体以实际 EVT 为准
2.1 与配对有关的代码说明
配对绑定配置代码:
在官方 EVT 从机示例中,关于配对绑定有关的配置在Peripheral_Init() 里面,如下:

再次说明,只要完成上面配置,配对绑定流程由 BLE 协议栈自动执行,自动完成配对、加密、保存 LTK/EDIV/Rand 到 Flash,下次回连自动加密。应用层只需配置 + 监听回调。
其中每个配置参数说明如下:
-
1. GAPBOND_PERI_DEFAULT_PASSCODE密码 ,6 位数字 -
2. GAPBOND_PERI_PAIRING_MODE其中有三个配置: GAPBOND_PAIRING_MODE_NO_PAIRING禁止配对GAPBOND_PAIRING_MODE_WAIT_FOR_REQ被动等待配对,等主机自己发起才配对GAPBOND_PAIRING_MODE_INITIATE连接成功 瞬间,就发送 Security Request 请求主机发起配对 -
3. GAPBOND_PERI_MITM_PROTECTION前面讲过的 MITM:中间人攻击,MITM = TRUE 时要求防中间人,必须输密码、或数字比对,不能随便就连。MITM = FALSE 时可以用 Just Works 直接配对。 -
4. GAPBOND_PERI_IO_CAPABILITIESIO 能力配置,5 种配置如下: GAPBOND_IO_CAP_DISPLAY_ONLY只有显示屏,没有输入,不能确认,按键(智能手环、电子标签)配对方式 Passkey Entry:显示6位数字,对方输入GAPBOND_IO_CAP_DISPLAY_YES_NO有显示屏 + 有确认键(带按键的显示器)配对方式 Numeric Comparison:双方显示数字,用户确认是否相同GAPBOND_IO_CAP_KEYBOARD_ONLY只有键盘输入,没有屏幕(蓝牙键盘)配对方式 Passkey Entry:对方显示数字,本机输入GAPBOND_IO_CAP_NO_INPUT_NO_OUTPUT无显示无输入无按键(耳机,传感器)配对方式 Just Works:无密码,直接配对GAPBOND_IO_CAP_KEYBOARD_DISPLAY既能显示又能输入(手机,平板)配对方式 所有方式都支持,协议自动选择最优 -
5. GAPBOND_PERI_BONDING_ENABLED是否需要绑定,= TRUE 需要绑定
上面 IO 能力配置,我感觉还是表格查询比较直观,补充一个表格:
|
|
|
|
|
|
GAPBOND_IO_CAP_DISPLAY_ONLY |
|
|
|
Passkey Entry
|
GAPBOND_IO_CAP_DISPLAY_YES_NO |
|
|
|
Numeric Comparison
|
GAPBOND_IO_CAP_KEYBOARD_ONLY |
|
|
|
Passkey Entry
|
GAPBOND_IO_CAP_NO_INPUT_NO_OUTPUT |
|
|
|
Just Works
|
GAPBOND_IO_CAP_KEYBOARD_DISPLAY |
|
|
|
|
配对绑定数据存储:
我们数据保存存放在哪里,这个在之前博文《沁恒 RISC-V 蓝牙芯片 Flash 分区管理及操作》 中有过说明:

在工程目录 HAL —> include —> CONFIG.h 文件里面有关于配对绑定存储地址,数量有关的宏定义配置:

具体细节一点大家可以自行查看或者查看之前的 Flash 分区使用说明文章,但是要记得,之前文章讲过我们修改宏定义,尽量在 MRS 工程配置里面修改:

2.2 示例修改测试
2.2.1 增加配对相关回调函数
官方从机示例中,只有配对绑定的配置,没有注册回调函数,我们为了观察效果,需要把回调函数加上,可参考主机例程 Central 的配对绑定回调函数 。
配对绑定状态回调相关的结构体如下:
typedefstruct{ pfnPasscodeCB_t passcodeCB; //!< Passcode callback pfnPairStateCB_t pairStateCB; //!< Pairing state callback pfnOobCB_t oobCB; //!< oob callback} gapBondCBs_t;
在从机示例种,我们先只加一个状态回调:
// GAP Bond Manager Callbacksstatic gapBondCBs_t Peripheral_BondMgrCBs = {NULL, // Passcode callback (not used by application) My_Peripheral_PairStateCB, // Pairing / Bonding state Callback (not used by application)NULL// oob callback};
实现一下回调函数 :
staticvoidMy_Peripheral_PairStateCB( uint16_t connectionHandle, uint8_t state, uint8_t status){switch(state) {case GAPBOND_PAIRING_STATE_STARTED: PRINT("配对开始\r\n");break;case GAPBOND_PAIRING_STATE_COMPLETE: if(status == SUCCESS) { PRINT("配对成功\r\n"); }else { PRINT("配对失败\n"); }break;case GAPBOND_PAIRING_STATE_BONDED:if(status == SUCCESS) { PRINT("绑定成功\r\n"); }break;case GAPBOND_PAIRING_STATE_BOND_SAVED:if(status == SUCCESS) { PRINT("绑定信息已保存到Flash\r\n"); }else { PRINT("绑定信息保存失败: %d\n", status); } break; }}
为什么测试先只加一个回调函数,因为如果第一个回调函数 passcodeCB 也增加实现,主机需要输入的密码会出现变化,不再以初始化的密码为准。而是需要在回调函数中生成密码。 这个我们下文也会测试。
我们把密码修改一下:
uint32_t passkey = 123456; // passkey "000000"
为了方便区分,我把蓝牙名称也修改了一下,这个大家随意。
然后烧录程序以后,通过手机蓝牙(不是 APP,不是 BLE 调试助手,nRF Connect 那些APP),是手机系统设置里面的蓝牙,选中从机设备,进行配对:

在从机 Debug 信息输出如下 :

如果我们要增加配对密码回调函数,再次说明,只要 Passcode callback 注册了,那么密码就不是初始设定的密码了:

我们可参考官方 EVT 中主机代码中 centralPasscodeCB 程序写密码回调函数,如下:
// 配对密码回调(如果需要自己显示密码就用)staticvoidMy_Peripheral_PasscodeCB(uint8_t *deviceAddr, uint16_t connHandle, uint8_t uiInputs, uint8_t uiOutputs){uint32_t passcode;// Create random passcode passcode = tmos_rand(); passcode %= 1000000;// Display passcode to user // passcode = 123456; 我们也可以再这里固定密码if(uiOutputs != 0) { PRINT("Passcode:%06d\n", (int)passcode); }// Send passcode response GAPBondMgr_PasscodeRsp(connHandle, SUCCESS, passcode);}
回调函数设置如下:
// GAP Bond Manager Callbacksstatic gapBondCBs_t Peripheral_BondMgrCBs = { My_Peripheral_PasscodeCB, // Passcode callback (not used by application) My_Peripheral_PairStateCB, // Pairing / Bonding state Callback (not used by application)NULL// oob callback};
我们再进行上面配对操作,就需要注意新生成的密码,手机输入的秘密以新生成的密码为准(当然,我们可以在密码回调中固定一个密码也可以),如下:

那在上文中,我们讲配对绑定配置的时候,讲了不同配置下的配对方式会有不同,这个大家都可以自己尝试一下,比如我们把示例中 ioCap 设置为无输入输出的情况:
uint8_t ioCap = GAPBOND_IO_CAP_NO_INPUT_NO_OUTPUT;
我们手机进行配对的时候,就不需要输入密码,点击配对即可成功配对。

2.2.2 关于回连
只要 bond 成功,设备就具备自动回连的能力,但手机系统不会像对待耳机/HID 那样自动连接普通 BLE 设备。
所以我们上面的示例测试,断开并不能够实现它自动回连的效果,那么我们可以把我们自己的设备模拟成 HID 设备,测试一下自动回连。这里我们为了方便,直接使用 EVT 例程中的 HID 设备测试回连,关于 HID 设备后期会有专门的博文进行说明,本文不讨论 HID 设备实现与逻辑等问题。
比如我们找到 BLE 目录下面的 HID_Mouse 例程:

我们本次需要关注的点在于配对绑定这一块,这个根据上文说明就能看懂:

示例直接通过手机蓝牙测试 :

2.2.3 主机示例配对绑定测试
主机和从机需要配对绑定,会根据它们配置的 IO 能力进行配对方式的选择, 在配对的时候,是根据双方的能力共同决定的,所以只需要知道双方能力的组合,就能确定它们的配对方式,如下表格:
|
|
|
|
KEYBOARD_DISPLAY
DISPLAY_ONLY |
|
|
KEYBOARD_DISPLAY
KEYBOARD_ONLY |
|
|
KEYBOARD_DISPLAY
KEYBOARD_DISPLAY |
|
|
NO_INPUT_NO_OUTPUT
|
|
|
KEYBOARD_ONLY
DISPLAY_ONLY |
|
|
DISPLAY_ONLY
DISPLAY_ONLY |
|
|
KEYBOARD_ONLY
KEYBOARD_ONLY |
|
|
对于我们的示例而言,从机默认为 GAPBOND_IO_CAP_DISPLAY_ONLY 所以当主机为 NO_INPUT_NO_OUTPUT 或者 DISPLAY_ONLY 时,它们配对是不需要密码的,不会进入密码回调,比如主机设置如下(注意主机要设置为 GAPBOND_PAIRING_MODE_INITIATE 连接后发起配对):
/ Setup the GAP Bond Manager{uint32_t passkey = 0;uint8_t pairMode = GAPBOND_PAIRING_MODE_INITIATE;uint8_t mitm = TRUE;uint8_t ioCap = GAPBOND_IO_CAP_NO_INPUT_NO_OUTPUT;uint8_t bonding = TRUE; GAPBondMgr_SetParameter(GAPBOND_CENT_DEFAULT_PASSCODE, sizeof(uint32_t), &passkey); GAPBondMgr_SetParameter(GAPBOND_CENT_PAIRING_MODE, sizeof(uint8_t), &pairMode); GAPBondMgr_SetParameter(GAPBOND_CENT_MITM_PROTECTION, sizeof(uint8_t), &mitm); GAPBondMgr_SetParameter(GAPBOND_CENT_IO_CAPABILITIES, sizeof(uint8_t), &ioCap); GAPBondMgr_SetParameter(GAPBOND_CENT_BONDING_ENABLED, sizeof(uint8_t), &bonding);}
测试结果如下,从机不进入密码回调函数,使用的是 Just Works 配对模式:

如果我们把主机设置为:
uint32_t passkey = 0;uint8_t pairMode = GAPBOND_PAIRING_MODE_INITIATE;uint8_t mitm = TRUE;uint8_t ioCap = GAPBOND_IO_CAP_KEYBOARD_ONLY;uint8_t bonding = TRUE;
当然还需要修改一下 centralPasscodeCB 函数,把从机对应的密码写上去,从机这个地方也要固定密码,要不然不好测试:
staticvoidcentralPasscodeCB(uint8_t *deviceAddr, uint16_t connectionHandle,uint8_t uiInputs, uint8_t uiOutputs){uint32_t passcode;// Create random passcode passcode = tmos_rand(); passcode %= 1000000; passcode = 520131; //和从机一样,如果有密码回调,就在密码回调里面设置密码// Display passcode to userif(uiOutputs != 0) { PRINT("Passcode:%06d\n", (int)passcode); }// Send passcode response GAPBondMgr_PasscodeRsp(connectionHandle, SUCCESS, passcode);}
我们再来看看测试结果,会进入密码回调函数,使用的是 Passkey Entry 配对方式 :

如果密码不对,配对失败的情况(示例总,配对失败并不影响 notify 与数据读写,因为示例读写的特征值不需要加密读写):

2.3 其他说明
-
1. HID 类型设备需要修改广播包,这个后面会有博文讨论 HID 类型设备; -
2. 开发板不方便输入密码,所以开发板模拟某些设备的时候需要注意一下 IO 能力的合理配置。 -
3. 配对一般建议设置为示例默认的等待主机配对 GAPBOND_PAIRING_MODE_WAIT_FOR_REQ,防止部分手机不接收从机发起的配对请求。 -
4. 示例中主机从机绑定过后,如果某一方重新烧录程序删除了 Flash 内容(删除了保存的绑定信息),需要将双方的绑定信息都删除再进行正常通信重新绑定,否则会出现问题。
结语
本文我们了解了一下配对绑定相关知识,也说明了沁恒微 EVT 代码中与配对绑定相关的代码, 希望能够帮助大家更好的理解 Ble 中的配对绑定相关问题。
好了,本文就到这里。谢谢大家!
夜雨聆风