使用的例程目录:nrf5sdkformeshv500src\examples\light_switch\client
抓包使用的软件:Wireshark
抓包方法:使用Nordic的nRF52840 Dongle配合Wireshark对蓝牙设备抓包(BLE)
配网过程主要分为五个阶段:
信标阶段就是让配网器发现并选择周边未配网设备,然后建立连接的过程。
支持PB-ADV的未配网设备会持续一定时间以某个频率向外广播未配网设备信标,以让周边的配网器能够发现这个未配网设备,未配网设备信标中包含这个设备的UUID。配网器收到未配网设备的信标之后,会和未配网设备建立一条链接,然后进行配网。蓝牙mesh规范中规定PB-ADV和PB-GATT都可以承载链接,比如可以直接使用Nordic官方的mesh app配网,这个时候这个链接就是PB-GATT承载的。
由未入网设备发起的信标,配网器收到信标后,可以与该设备进行配网操作。格式定义:
Beacon Data部分字段结构:
字段定义:
抓包结果:
广播地址:广播地址使用的是MCU的FICR寄存器的DEVICEADDR[0]和DEVICEADDR[1]的低16位,从MCU手册中获取信息如下:
对应例程advertiser.c中的advertiser_address_default_get()函数:
UUID:UUID使用的是MCU的FICR寄存器的DEVICEID[n]和DEVICEADDR[n](n为0或1),同时需要修改版本和变体位。
对应例程nrf_mesh_configure.c中的nrf_mesh_configure_device_uuid_reset()函数:
通过观察可以发现,设备发送未配网信标的时间间隔为2s,这个参数是由NRF_MESH_PROV_BEARER_ADV_UNPROV_BEACON_INTERVAL_MS宏定义决定的,位于nrf_mesh_config_prov.h中,并在prov_bearer_adv.c的send_unprov_beacon()函数中调用。
配网承载控制PDU用于管理承载上的会话,因为承载本身没有会话管理功能。数据格式:
Link Open消息如下:BearerOpcode为0x00,GPCF为0b11,Device UUID为要选择的未配网设备信标中的UUID。
Link Close消息如下:Reason字段表示链接关闭的原因。
Reason字段可能的值:
邀请阶段主要是交互诸如设备能力等信息,这些信息用于后续的配网操作。
配网邀请(Provisioning Invite)由配网器发给未配网设备,字段如下:
提示时间(Attention Duration)长度为1字节,0表示关闭,非0表示剩余的时间,单位为秒。对于不支持提示时间的设备,提示时间应该设置为0,如果一个元素的提示时间为非0值,则提示时间状态为打开,并按秒递减至0。就是待配网设备可以用这个参数的时间通过LED,蜂鸣器这种的来指示状态。
配网器发起邀请PDU,提示时间为5s:
未配网设备收到消息后需要响应配网能力。配网能力(Provisioning Capabilities)由未配网设备发送给配网器,字段如下:
Number of Elements:表示设备含有的元素个数。
例程中宏定义位于nrf_mesh_config_app.h中。
Algorithm:配网算法,目前只支持ECDH(椭圆曲线迪菲-赫尔曼秘钥交换)。
Public Key Type:设备的公钥类型,是否支持OOB。
Static OOB Type:设备是否支持静态OOB。
Output OOB Size:输出式OOB的最大信息长度。
Output OOB Action:输出式OOB的输出动作类型。
Input OOB Size:输入式OOB的最大信息长度。
Input OOB Action:输入式OOB的输入动作类型。
待配网设备收到配网邀请后在例程中prov_provisionee_pkt_in()函数中处理,如果处理成功会通过send_capabilities()函数来响应配网能力。
未配网设备响应配网能力,3个元素,支持静态OOB:
配网器与未配网设备配网时,需要发布网络密钥与地址,这些信息与mesh网络的安全性有关。因此要求配网双方进行身份验证。蓝牙mesh使用非对称密钥算法为著名的ECDH算法,可以在仅交换公钥的情况下协商出外部无法获知的密钥。
如果不使用OOB公钥,设备双方应该交换公钥。
否则,如果公钥可以通过OOB机制(双方通过二维码、NFC等方式)获得,则提供方应生成新的密钥对,生成的密钥对的公钥应从提供方传输到设备,并使用适当的OOB技术从设备读取静态公钥。
配网开始(Provisioning Start)由配网器发送给未配网设备,告知未配网设备交换公钥流程开始,同时从前面配网能力(Provisioning Capabilities)消息中选择具体方式。数据格式如下:
Algorithm:配网算法模式,目前只支持ECDH(椭圆曲线迪菲-赫尔曼秘钥交换)。
Public Key:设备端是否使用OOB公钥。
Authentication Method:身份认证模式选择。
Authentication Action:OOB模式下的认证行为。
Authentication Size:OOB模式下的认证长度。
配网器发起配网开始PDU,没有OOB公钥,使用静态OOB认证:
在例程中未配网设备收到Provisioning Start消息后,在prov_provisionee_pkt_in()函数中处理:
配网双方交换公钥后需要验证收到的公钥的有效性,如果验证为无效的公钥,则退出配网流程。
配网器交换公钥PDU:
在例程中未配网设备收到Provisioning Public Key消息后,在prov_provisionee_pkt_in()函数中处理,switch分支为PROV_PDU_TYPE_PUBLIC_KEY,如果不使用OOB公钥,则未配网设备发送自己的公钥。
未配网设备交换公钥:
当双方公钥验证结束后,进入身份认证阶段。
在蓝牙mesh规范中,身份认证过程包含了设备端和配网器的认证,两者均会和对方交换一个Confirmation Value,以此生成此Confirmation Value的Random Value。Confirmation Value的计算使用了ECDH密钥、配网交互数据包及OOB认证消息。当一方收到完整的Confirmation Value和Random Value,会根据ECDH密钥、配网交互数据包及OOB认证消息对Random Value重新计算,然后生成一个Confirmation Value,与收到的Confirmation Value比较,如果相同则认证成功,失败则退出配网。两者均验证成功,则整个认证流程结束。
根据配网器与未配网设备的输入输出能力,蓝牙mesh规范中定义了三种认证方式:
①输出式OOB认证
②输入式OOB认证
③静态OOB或无OOB认证
这三种方式命名我的理解是都相对于未配网设备的来说的。
输出式OOB认证要求未配网设备能够有显示随机数的能力,比如通过LED闪烁,或者屏幕显示;同时配网器要有比如按键这种的来输入识别到的随机数。
输入式OOB认证要求配网器能够有显示随机数的能力,比如通过LED闪烁,或者屏幕显示;同时未配网设备要有比如按键这种的来输入识别到的随机数。
静态OOB认证双方交换Confirmation Value和Random Value,对于没有有效的输入输出方式的设备来说,是比较安全友好的认证方式;无OOB认证比较危险,无法得知是否存在中间人,使整个mesh网络遭受攻击。
输出式OOB认证和输入式OOB认证就是我们之前连接蓝牙的时候根据屏幕上显示的随机数输入,然后进行蓝牙配对。
配网确认值(Provisioning Confirmation)消息PDU由配网器或者未配网设备发给对端设备。数据格式:
配网器发送配网确认值PDU:
在例程中未配网设备收到Provisioning Confirmation消息后,在prov_provisionee_pkt_in()函数中处理,switch分支为PROV_PDU_TYPE_CONFIRMATION,未配网设备发送自己的确认值。
待配网设备发送配网确认值PDU:
配网随机数(Provisioning Random)消息PDU由配网器或者未配网设备发给对端设备。数据格式:
配网器发送随机数PDU:
在例程中未配网设备收到Provisioning Random消息后,在prov_provisionee_pkt_in()函数中处理,switch分支为PROV_PDU_TYPE_RANDOM,未配网设备发送自己的随机数。
待配网设备发送随机数PDU:
在配网过程中,最后也是最重要的一个阶段是分发配网数据。在此阶段中,配网器负责生成并分发配网数据到未配网设备。配网数据包括如’'Network Key(网络密钥)"、"Unicast Address(单播地址)"等重要数据项。
为了安全的分发配网数据,配网器需要使用AES-CCM算法来加密配网数据,此加密算法涉及两个加密密钥参数:Session Key和Session Nonce,这两个参数均由ECDH派生。
分发配网数据(Provisioning Data)PDU格式如下:
Encrypted Provisioning Data:加密配网数据字段如下:
配网器分发配网数据:
在例程中未配网设备收到Provisioning Data消息后,在prov_provisionee_pkt_in()函数中处理,switch分支为PROV_PDU_TYPE_DATA。
配网完成(Provisioning Complete)PDU由节点(接收到配网数据后,未配网设备变成mesh网络中的节点)发送给配网器,无参数,在例程中switch分支为PROV_PDU_TYPE_DATA中处理完成后发送:
配网失败(Provisioning Failed)PDU由未配网设备发送给配网器。数据结构如下:
错误码一共9种,禁止配网、无效PDU、无效格式、非预期PDU、确认失败、资源不足、解密错误、非预期的错误和无法非配地址。错误码对应值如下:
网络安全信标(Secure Network beacon)用于让mesh节点明确子网及自身的安全状态,在IV Index Update以及Key Refresh过程中被使用。数据格式:
字段说明:
Flags字段定义:
子网中节点在收到网络安全信标之后,应该首先对其鉴权,鉴权完成后,数据包被接收。两个连续Beacon的发送间隔被称为Beacon Interval,在具体实现中,为了防止mesh网络中因含有过多的Beacon而过载,在设置Beacon Interval应该有合理的退避过程。理想状态是,一个子网节点大概每10s收到一个Beacon。
对于每个子网,应该分别设定Beacon Interval。节点应该持续记录一段时间(Observation Period)内的Beacon数量,然后按照下面的公式来计算Beacon Interval。
Beacon Interval = Observation Period * (Observed Number of Beacons + 1) / Expected Number of Beacons
其中Observation Period应该是常规Beacon Interval的两倍,Observed Number of Beacons是在Observation Period内记录到的Beacon数量,Expected Number of Beacons应该是Observation Period除以10s。
如果计算出来的结果小于10s,则将Beacon Interval设置为10s,如果计算出来的结果大于600s,则将Beacon Interval设置为600s。
网络安全信标抓包:
网络安全信标间隔Beacon Interval:
例程中默认的网络信标间隔为10s,宏定义是NRF_MESH_BEACON_SECURE_NET_BCAST_INTERVAL_SECONDS,位于nrf_mesh_define.h。
Beacon Interval更新需要的相关参数及计算位于net_beacon.c中:
Beacon Interval更新函数位于net_beacon.c中:
网络安全信标初始化函数也位于net_beacon.c中: