一文读懂BLE
时间:2023-08-12 19:37:01
BLE名称概念
- BR/EDR:Basci Rate/Enhanced Data Rate,关注基本速率和增强速率技术
- LE:Lower Energy,低功耗技术,注重功耗
- LQI和RSSI:LQI为Link Quality Indication,信号质量指示;RSSI为Received Signal Strength Indication,信号强度指示。LQI注意正确率(噪音)noise干扰),RSSI注意信号强度
- L2CAP:Logical Link Control and Adaptation Protocol,不同的应用程序可以共享相同的逻辑链路
- 物理层:物理类似于城市的各种道路。Physical Channel真实的物理实体频段是物理信道;Physical Links物理链路是物理信道的封装
- 逻辑层:逻辑层类似于快车道、慢车道等。Logic Links逻辑链路;Logic Transport为逻辑传输
- Profile:蓝牙定义的各种应用服务,如SPP,HFP等
- GAP:Generic Access Profile,是蓝牙的基础profile,用于蓝牙设备的一般访问功能,包括设备发现、连接、、服务发现等
- ATT:Attribute Protocol,属性协议确定了客户端和服务端之间的属性值发现、读取、写入等操作
- GATT:Generic Attribute Profile,profile框架
- HCI:Host Controller Interface,仅仅是将Link Layer提供的功能包装成特定的Command和Event,没有逻辑可言
- SM:Security Manager,安全管理规定了与蓝牙安全通信相关的所有因素
- SMP:Security Manager Protocol,安全管理协议规定配对和密钥分发交互
- OOB:Out Of Band,通过其他方式交换密钥,如带外NFC,WIFI等
- MITM:Man In The Middle,中间人是设备双方通信过程中的第三方
- **LE legacy pairing **:与遗留配对相比,LESC来说的。
- LESC:LE Secure Connections,蓝牙4.2.未来引入的新密钥生成方法和验证方法(ECDH)
- TK:Temporary Key,只要是临时密钥legacy pairing方式中
- STK:Short Term Key,短期密钥也只用于legacy pairing方式中
- LTK:Long Term Key,长期没有,legacy pairing和LESC将使用所有的方法LTK。
- IRK:Identity Resolving Key,蓝牙设备地址分析密钥用于分析随机地址,以确保这些地址来自同一设备
- CSRK:Connection Signature Resolving Key,连接签名分析密钥,用于签名连接中的通信数据,另一方可以检查签名值。
BLE协议框架
BLE协议可分为Bluetooth Application和Bluetooth Core两部分,而Bluetooth Core又包含BLE Controller和BLE Host两部分。
Controller
Controller实现与射频相关的模拟和数字部分,完成最基本的数据发送和接收,Controller对外界面为天线,对内界面为主机控制器界面HCI(Host controller interface);控制器包含物理层PHY(physicallayer),链路层LL(linker layer),主机控制器接口HCI。
Physical Layer
物理层定义通信介质(物理通道),BLE定义了40个RF Channel,每个带宽为2MHz,频率范围为2402-2480MHz。
Link Layer
链路层主要负责链路管理、链路控制、数据收发、物理通道控制RF收发相关参数。解决物理信道共享问题;为通信实体建立独家逻辑链路(Logical Link);确保数据传输的可靠性,提供验证、重传等机制。
HCI
HCI定义Host和Controller之间(通常是两个IC)通信协议,如UART、USB、SDIO等等。为什么这两层之间没有函数调用?BLE由于产品形式的不同,只有全协议栈Controller,只有Host层。
Host
主机Host是蓝牙协议栈的核心部分。
GAP
用于蓝牙设备的一般访问功能,包括设备发现、连接、、服务发现等
L2CAP
控制和适应逻辑链路协议,实现不同的应用程序可以共享相同的逻辑链路。作用为协议/通道的多路复用,负责上层应用数据(L2CAPService Data Units,SDUs)协议数据单元的分割(和重组)生成(L2CAP Packet Data Units,PDUs),为满足用户数据传输对延迟的要求,便于实现后续的重传、流控等机制。
ATT/GATT
ATT属性协议定义了一个通信的基本框架,数据的基本结构,以及通信的指令,而GATT层就是service和characteristic这些,GATT层用于给每个数据一个具体的内涵,使数据具有结构性和意义。换句话说,没有GATT低功耗蓝牙也可以通信,但会产生兼容性问题和低效率的通信。
SM
安全管理规定了所有与蓝牙安全通信相关的因素。
Application
在蓝牙核心协议的基础上,定义了各种蓝牙应用协议层profiles/services各种具体应用。
BLE通信连接
BLE设备双方通信按状态机切换,连接方按状态机切换Standby->Advertising->Connection状态顺序连接;主动连接的一方按照状态顺序连接;Standby->-Scanning->Initiating->Connection连接状态顺序。蓝牙发起广播的一方称为广播员,发起扫描的一方称为扫描员。双方成功连接后,分别成为Slave和Master两种角色。Master角色设备称为Central设备,而Slave角色设备称为Peripheral设备。由于Slave角色设备一般提供服务,因此称为服务端;Master因此,角色设备访问服务被称为客户端。
1. 从机发起广播
蓝牙广播者Advertiser发起广播时,扫描者和初始化者可以接收其广播数据,广播数据可以在37、38、39三个信道上广播。
广播数据
BLE该协议规定,广播数据不得超过31字节,其数据是一个接一个的AD Structure组成,每个AD Structure组成(LTV形式)由1字节的长度和剩余数据组成,如下:
在Data下又划分成AD Type和AD Data两个域,BLE根据不同的使用场景定义不同的协议AD Type,如:0x01为Flags,指示设备支持Limited Discoverable Mode/General Discoverable Mode的能力,如AD Data 0x06是通用发现模式,不支持BR/EDR;0x09为Complete Local Name;0xFF制造商数据等
扫描响应数据
扫描应包同广播数据格式相同,当扫描者为了请求广播者更多的数据,就会发送扫描请求包SCAN_REQ,广播者收到该请求后,可以携带更多信息回复扫描响应数据。
广播参数
广播参数包括广播间隔,广播类型,地址类型,信道,过滤策略等的配置。
-
广播类型
可连接非定向广播ADV_IND;可连接定向广播ADV_DIRECT_IND_LOW和ADV_DIRECT_IND_HIGH(包括低占空比和高占空比两种);可扫描非定向广播ADV_SCAN_IND;不可连接非定向广播ADV_NONCONN_IND
-
广播间隔
对于非定向广播和可连接的低占空比广播,广播事件的间隔由advInterval和advDelay组成。advInterval是N*0.625ms,N的范围为0x0020-0x4000,即20ms到10.21s之间,advDelay是协议层控制的0-10ms间的随机数。如果可扫描的非定向广播或者不可连接的非定向广播,advInterval不低于100ms。
而对于高占空比的定向广播,广播周期小于等于3.75ms,但是广播时间不能超过1.28s,即最长1.28s后需要退出广播状态。
-
地址类型
BLE地址分为:Public Device Address和Random Device Address,而Random Device Address又分为Static Device Address和Private Device Address;Static Device Address上电随机生成,而Private Device Address通过定时更新和地址加密的方法。其根据地址是否加密,又分为Non-resolvable Private Address和Resolvable Private Address两类。
广播参数中需要设置自身地址类型和对端地址类型及地址
-
信道
可以设置广播包的信道,只37,38,39信道一种或者所有
-
广播过滤策略
广播过滤策略分为:只接受位于白名单中的设备的扫描和连接请求;可以接受任何设备的扫描和连接请求;可以接受任何设备的扫描请求,但只接受位于白名单中的设备的连接请求;可以接受任何设备的连接请求,但只接受位于白名单中的设备的扫描请求。
开启广播
设置完成广播参数和广播数据后,就可开启广播,等待扫描者进一步扫描和连接处理。
2. 主机发起扫描
蓝牙扫描者发起扫描,接收广播者发送的广播。
扫描参数
扫描参数包括扫描模式,自身地址类型,扫描过滤策略,扫描间隔及窗口等
-
扫描模式
主动扫描(Active Scanning)和被动扫描(Passive Scanning)。被动扫描只听不问,即接收广播包,不发送扫描请求SCAN_REQ;而主动扫描还会发送扫描请求SCAN_REQ,并接收后续的SCAN_RSP。
-
扫描过滤策略
接收所有广播包除了没有包括自身地址的定向广播(定向广播是让指定的对端地址连接);只接收设备地址在白名单中的广播,没有包括自身地址的定向广播也进行忽略;接收所有非定向广播、Initiator Addresss是可解析私有地址的定向广播和包括自身地址的定向广播;接收设备地址在白名单中的广播、Initiator Addresss是可解析私有地址的定向广播和包括自身地址的定向广播
-
扫描间隔
两次扫描间的时间,扫描间隔等于N*0.625ms,N的范围为0x0004-0x4000,即2.5ms至10.24s之间
-
扫描窗口
单次扫描的持续时间,扫描窗口也等于N*0.625ms,N的范围为0x0004-0x4000,即2.5ms至10.24s之间,扫描窗口要小于等于扫描间隔
-
自身地址类型
同广播地址类型定义
开启扫描
设置完成扫描参数后,就可开启扫描,并设置扫描的超时时间。在扫描窗口内,如果扫描到广播包数据,就可解析广播者携带的flag、广播设备名、制造商等广播数据。如果该广播可以连接,并且是自己想连接的设备,即可发起连接,并停止扫描。
3. 主机发起连接
主动连接的设备(Initiator)在收到广播包之后,回应一个CONNECT_REQ请求,该请求携带了可决定后续“通信时序”的参数。Initiator在发出CONNECET_REQ请求后,自动转变为连接状态,成为Master角色。
4. 主从连接成功
广播者Advertiser在收到CONNECT_REQ请求之后,也自动转变为Connection状态,成为Slave角色。主从双方按照CONNECT_REQ参数所给出的约定,定时到切换到某一个Physical Channel上,按照Master->Slave然后Slave->Master的顺序,收发数据,直至连接断开。
连接参数
连接参数用于保证主从双方连接时序,包括
-
最小连接间距
最小连接间距等于N*1.25ms,N的范围为6-3000,即7.5ms至4s
-
最大连接间距
最大连接间距等于N*1.25ms,N的范围为6-3000,即7.5ms至4s
-
从机延迟
定义从机可以忽略多少个连接事件(从机可以睡眠省电,不会导致超时断开),范围为0-500,但必须满足
(1 + connSlaveLatency) * connIntervalMax * 2 <= connSupervisionTimeout
-
监控超时
监控超时等于N*10ms,N的范围为10-3200,即100ms至32s
对于IOS设备,连接参数要求更为严格,即
connIntervalMax * (1 + connSlaveLatency) <= 2s
connIntervalMin >= 20ms
connIntervalMin + 20ms <= connIntervalMax
connSlaveLatency <= 4
connSupervisionTimeout <= 6s
connIntervalMax * (1 + connSlaveLatency) * 3 < connSupervisionTimeout
连接超时及断开
主从连接断开原因有两种:预期内的主动断开,会走Termination Procedure过程;非预期的异常断开,如严重干扰,突然断电,距离过远等。第一种情况主从按照协议预定正常断开;而第二种情况对于从机来说,可以立马检测到异常断开,对于主机来说需要等到timeout后才会引发超时断开。
5. 主机发现服务
BLE蓝牙设计的初衷是应用在物联网上,物联网最广泛的应用就是信息采集,如温度、湿度、压力、PM2.5等等。
ATT
针对信息采集的需求,BLE抽象出一个协议ATT:Attribute protocol,该协议将这些“信息”以“Attribute(属性)”的形式抽象出来,并提供一些方法,供远端设备(remote device)读取、修改这些属性的值(Attribute value)。
ATT协议定义了两种角色:ATT Server提供信息(属性),ATT Client访问信息(属性)。一个Attribute由四个元素组成:
- attribute type:属性类型,使用UUID区分定义,标识我们常说的温度、湿度、电池等。SIG对常⽤用的一些数据类型进了归类,赋予不同的数据类型不同的标识码 (UUID)。例如 0x2A09 表示电池信息, 0x2A6E 表示温度信息。
- attribute handle:属性句柄,16位值,协助找到相应的属性,正如可以使⽤用内存地址查找内存中的内容一样 。
- attribute value:属性值是每个属性真正要承载的信息,其他三个元素都是为了让对方能够更好地获取属性值。
- attribute permissions:属性许可,每个属性对各自的属性值有相应的访问限制,比如有些属性是可读的、有些是可写的、有些是可读又可写的等等。拥有数据的一方可以通过属性许可,控制本地数据的可读写属性。
属性句柄 | 属性类型 | 属性值 | 属性许可 |
---|---|---|---|
0x0001 | |||
0x0002 | |||
…… | |||
0xFFFE | |||
0xFFFF |
服务端和客户端之间常用数据操作:
-
客户端发送数据给服务端
通过对服务器的数据进行写操作 (Write),来完成数据发送工作。写操作分两种,一种是写入请求 (Write Request),一种是写入命令 (Write Command),两者的主要区别是前者需要对方回复响应 (Write Response),而后者不需要对方回复响应。
-
服务端发送数据给客户端
主要通过服务端指示 (Indication) 或者通知 (Notification) 的形式,实现将服务端更新的数据发给客户端。与写操作类似,指示和通知的主要区别是前者需要对方设备在收到数据指示后,进行回复 (Confirmation)。
-
客户端读取服务端数据
GATT
ATT只定义client和server按照Attribute的形式共享信息,即规定了在 BLE 中的最小数据存储单位 ,但是具体共享哪些信息是由GATT决定的。GATT 规范则定义了如何⽤用特性值和描述符表示一个数据,如何把相似的数据聚合成服务 (Service),以及如何发现对端设备拥有哪些服务和数据。GATT是一个profile framwork,用于提供通用的、信息的存储和共享等功能,其层次结构如下:
GATT层次结构从顶层到底层为:Profile -> Service -> Characteristic。
-
Profile是一个抽象的概念,它只是一个被Bluetooth SIG或者外设设计者预先定义的Service的集合,例如心率Profile(Heart Rate Profile)就是结合了Heart Rate Service和Device Information Service。
-
Service是把数据分成一个个的独立逻辑项,每个Service是有一个UUID唯一标识。UUID有16bit的,或者128bit的。16bit的UUID是官方通过认证的,需要花钱购买,128bit是自定义的,这个就可以自己随便设置。每个Service可以包含一个或者多个Characteristic。
以Heart Rate Service为例,SIG官方通过的16bit的UUID为**0x180D**,其包含3个Characteristic:Heart Rate Measurement,Body Sensor Location和Heart Rate Control Point。
-
Characteristic是最小的逻辑数据单元,由一个Properties、一个Value、一个或者多个Descriptor组成。Properties定义了characteristic的Value如何被使用,以及characteristic的Descriptor如何被访问;Value是特征的实际值,例如一个温度特征,其Characteristic Value就是温度值;Descriptor则保存了一些和Characteristic Value相关的信息,如用户描述,CCC,展示格式,单位格式。
与Service类似,每个Characteristic用16bit或者128bit的UUID唯一标识。你可以免费使用Bluetooth SIG官方定义的标准Characteristic,使用官方定义的,可以确保BLE的软件和硬件能相互理解。当然,你可以自定义Characteristic,这样的话,就只有你自己的软件和外设能够相互理解。以Heart Rate Measurement为例,其UUID为0x2A37。
注意:以上所有的Service、Characteristic、Characteristic Properties、Characteristic Value、Characteristic Descriptor等等,都是作为一个Attribute存在的。具备Attribute所有特征:Attribute Handle、Attribute Types、Attribute Value和AttributePermissions。
一个profile定义示例如下:
属性句柄 | 属性类型 |
---|---|
0x0001 | 服务1 |
0x0002 | 特性声明1 |
0x0003 | 特性数值1 |
0x0004 | 描述符1 |
0x0005 | 特性声明2 |
0x0006 | 特性数值2 |
0x0007 | 描述符2 |
0x0008 | 描述符3 |
0x0009 | 服务2 |
服务发现
参考:BLE发现过程、通用属性规范
服务Service的声明格式如下,服务Service可以是主要服务项(UUID=0x2800)或次要服务项(UUID=0x2801)。在绝大多数情况下均可以不使用次要服务项,仅使用主要服务。
服务发现有两种方案:发现全部服务;发现指定UUID服务。
-
发现全部主要服务
客户端设备发送Read By Group Type Request,起始句柄为0x0001,结束句柄为0xFFFF,属性类型为0x2800(Primary Service Declaration),查找全部符合条件的首要服务项。
服务端设备回应Read By Group Type Response,返回多个主要属性信息组成的列表,每个属性信息包括三个参数:元素长度,首尾句柄、属性的UUID。例如返回响应Generic Attribute服务:(0x06, 0x0001, 0x0005, 0x1801)分别代表元素长度6字节,起始句柄0x0001,结束句柄0x0005,UUID为0x1801(Generic Attribute)。即按服务声明格式如下所示:
Attribute Handle Attribute Type Attribute Value Attribute Permission 0x0001-0x0005 0x2800 0x1801 只读 由于列表长度不能超过属性层的PDU长度,因此可能需要发送多个请求,直至服务端设备返回“未找到属性项”或到达结束句柄,从而获取全部的主要服务项。
发现全部主要服务示例如下:
-
发现指定的主要服务
客户端设备发送Read By Type Value Request,起始句柄为0x0001,结束句柄为0xFFFF,属性类型为0x2800(Primary Service),指定的UUID如0x1801(Generic Attribute),查找符合指定条件的首要服务项。
服务端设备返回响应Find By Type Value Response,其包含了该指定UUID服务的首尾句柄,如起始句柄0x0001,结束句柄0x0005。
发现指定UUID的服务,可以快速找到目标服务,对于不关心的服务项可以快速略过,加快整体发现过程的速度。
发现指定的主要服务示例如下:
发现特征
发现特征是发现某个指定服务下的特征,因此一定要先发现服务,再发现特征。通过服务发现,已知服务的首尾句柄,如0x1801服务的起始句柄0x0001,结束句柄0x0005。在服务的句柄范围内搜索特征声明的UUID 0x2803(Characteristic),一旦找到就返回响应,响应中包含了特征的关键信息,包括特征的句柄范围,特征属性,特征值句柄,特征UUID,权限。特征声明格式如下。
同发现服务类似,发现特征也有两个方案:发现全部特征,发现指定UUID的特征。
-
发现全部特征
客户端设备发送Read By Type Request,设置已知的服务首尾句柄,如起始句柄0x0001,结束句柄0x0005,属性类型为特征项声明的UUID即0x2803(Characteristic),查找全部符合条件的特征项。
服务端返回Read By Type Response,响应中包含多个“属性句柄 – 值”元素组成的列表。单个元素包含三个参数:元素长度、特征项声明的句柄和特征值参数。特征值参数包括特征值的功能特性、特征值句柄和UUID。如返回响应Service Changed特征:(0x07, 0x0002, 0x10, 0x0003, 0x2A05)分别代表元素长度7字节,特征句柄为0x0002,特征功能特性为0x10,即Notify功能,特征值句柄为0x0003,特征值UUID为0x2A05,即Service Changed。即按照特征声明格式如下所示:
Attribute Handle Attribute Types Characteristic Properties Characteristic Value Attribute Handle Characteristic UUID Attribute Permissions 0x0002 0x2803 0x10 0x0003 0x2A05 只读 发现全部特征示例如下:
-
发现指定UUID特征
根据已知的特征项UUID和首末句柄范围,查找满足条件的特征项。如根据特征UUID 0x2A05(Service Changed)和起始句柄0x0001,结束句柄0x0005,属性类型为特征项声明的UUID即0x2803(Characteristic),查找符合指定条件的特征项。
发现指定UUID特征的示例如下:
不难发现,特征声明Characteristic declaration的Attribute Value字段包括Characteristic Properties,Characteristic Value Attribute Handle,Characteristic UUID三项。按照特征声明的Attribute Value字段格式如下:
-
特征特性
定义特征值如何使用,或者特征描述符如何获取。其定义位如下:
-
特征值句柄
特征值的属性句柄
-
特征值UUID
获取到了特征值属性句柄Characteristic Value Attribute Handle,服务端和客户端就可以进行读、写、Notify等数据操作。特征值声明格式如下:
发现描述符
紧跟特征之后,可能会存在特征描述符Characteristic Descriptor Declaration。描述符也是一种属性,它是特征项的一部分,用以提供特征值的额外信息。协议栈定义了6种不同的描述符,协议定义了六种描述符,如下:
属性类型 | UUID | 描述 |
---|---|---|
«Characteristic Extended Properties» | 0x2900 | 特征项的扩展描述符 |
«Characteristic User Description» | 0x2901 | 特征项的用户描述符 |
«Client Characteristic Configuration» | 0x2902 | 客户端特征项配置描述符 |
«Server Characteristic Configuration» | 0x2903 | 服务端特征项配置描述符 |
«Characteristic Presentation Format» | 0x2904 | 特征项数据格式描述符 |
«Characteristic Aggregate Format» | 0x2905 | 聚合特征项数据格式描述符 |
-
0x2900 扩展性描述符,用于Reliable Write和Writable Auxiliaries这两类写属性。
-
0x2901 用户描述符,用于给出该特征值的文字描述。
-
0x2902 客户端特征项配置描述符,简称为CCCD,客户端设备通过一个标志参数,设置该特征值能否发送通知和指示。如果该标志参数为0x0001,表示该特征值允许发送通知;如果该标志参数为0x0002,表示该特征值允许发送指示。如果该标志参数为0x0000,表示该特征值不能发送通知和指示。
-
每个特征项最多能包含一个CCCD,对于具有Broadcast、Notify和Indicate功能的特征项,必须拥有一个CCCD。在两个建立了绑定的设备中,断开连接不会丢失CCCD信息。
-
0x2903 服务端特征项配置描述符,服务端设备通过一个标志参数,设置该特征值是否在广播中发出。如果该标志参数为0x0001,则广播消息中应该包含该特征值;如果该标志参数为0x0000,则广播消息中不包含该特征值。
-
0x2904 特征值格式描述符,用于提供特征值的数据格式。可选的数据类型包括:Boolean、1/4字节、1/2字节、1字节、2字节、3字节、4字节、8字节、16字节、带符号整数、无符号整数、浮点数、字符串、结构体等。还可以指定数据的指数、单位、名字空间、描述信息等。特征格式声明如下:
特征格式描述符的属性值如下定义:
以温度特征为例(摘自Core_V4.2的Vol3 Part G附录A),温度特征值为0x028A=650,特征格式描述符为{0x0E, 0xFE, degree Celsius, 0x01, Outside}分别代表:Format为sint16,Exponent为-2,Unit为摄氏度,组织,描述。即该特征表示室外温度为6.5℃。
-
0x2905 聚合特征项格式描述符,专用于聚合特征项。所谓聚合特征值,是指多个特征值共同组合成一个数值,每个特征值仅是该聚合数值的一部分。
客户端发现服务端描述符流程如下:
- 客户端向服务端发送Find Information Request,设置已知的特征首尾句柄,查找全部符合条件的描述符。
- 服务端返回Find Information Response,响应中包含多个“属性句柄 – 值”元素组成的列表。单个元素包含三个参数:UUID格式、特征值的句柄和描述符的UUID。如果UUID格式参数等于1,表示描述符的UUID为2字节UUID,如果等于2,表示描述符的UUID为16字节UUID。
发现全部描述符的示例如下:
以具有Notify属性的Service Changed(0x2A05)特征为例,其一定会携带一个CCCD(Client Characteristic Configuration Declaration)描述符。Client Characteristic Configuration的描述符UUID为0x2902,主机发出描述符发现请求,从机返回响应,其响应包中给出属性句柄和UUID信息。CCCD声明格式如下:
CCC属性值的格式如下:
至此,服务的所有信息发现完毕。可以看出服务发现就是发现各个ATT协议属性(Service、Characteristic、Characteristic Descriptor等)的句柄Handle,有了句柄可以通过其获取到具体的值(类似通过指针获取数据一样)。
6. 主从数据交互
通过以上服务发现过程,客户端可以获取到服务端所有特征值句柄,那么就可以向服务端发送读取、写入命令。另外服务端也能向客户端发送通知和指示命令。
特征值读取
特征值读取包括四个子功能:读取特征值,按UUID读取特征值,读取长包特征值,读取多个特征值。
-
读取特征值
如果客户端已知特征值句柄,向服务端发送Read Request,请求该句柄的特征值。读取特征值的示例如下:
-
按UUID读取特征值
如果客户端只知道特征UUID,不知其句柄,向服务端发送Read By Type Request,需携带首尾句柄和特征UUID,读取特征值。按UUID读取特征值示例如下:
-
读取长包特征值
客户端已知特征值的句柄,但是特征值的长度大于(ATT_MTU-1),向服务端发送Read Request以读取前(ATT_MTU-1)个字节,然后发送Read Blob Request并设置合适的偏移量,以读取随后的(ATT_MTU-1)个字节,重复执行Read Blob Request直到服务端的Read Blob Response内容小于(ATT_MTU-1),表明该特征值完全被读取。读取长包特征值示例如下:
-
读取多个特征值
客户端已知多个特征值的句柄,向服务端发送Read Multiple Request,参数为多个特征值句柄。服务端返回Read Multiple Response,包含了多个指定的特征值数据。读取多个特征值示例如下:
特征值写入
特征值写入包括五个子功能:无需回应的写命令,无需回应的带签名的写命令,写请求,写长包请求,可靠的写请求。
-
无需回应的写命令
客户端已知特征值句柄,向服务端发送Write Command,参数为特征值句柄和待写入的特征值。该命令不需要服务端进行回应,数据长度不能超过(ATT_MTU-3)个字节。其示例如下:
-
无需回应的带签名的写命令
客户端已知特征值句柄,且ATT Bearer没有进行加密。向服务端发送Signed Write Command,并设置签名认证标志位,实现带签名的写命令。数据长度不能超过(ATT_MTU-3-12)字节,其中12表示认证签名的长度,如果超过,仅写入前(ATT_MTU-3-12)个字节。该命令也无需服务端返回响应。其示例如下:
-
写请求
客户端已知特征值句柄,向服务端发送Write Request,写入指定数据。数据长度不能超过(ATT_MTU-3)字节,如果超过,仅写入前(ATT_MTU-3)个字节。该命令需要服务端返回响应Write Response。其示例如下:
-
写长包请求
客户端已知特征值句柄,但待写入数据长度过长,向服务端发送Prepare Write Request,设置适当的偏移量,将数据发送至服务端缓存起来,数据发送完毕后,服务端发送Execute Write Request执行写请求。待写数据总长度不受限制,但是分步发送数据每次数据长度不得超过(ATT_MTU-3)。两种请求均需要对应的服务端响应。其示例如下:
-
可靠的写请求
客户端已知特征值句柄,希望数据的每个字节都必须被安全写入服务端设备,向服务端发送Prepare Write Request,偏移量永远等于0,一次性只发送一个数据,待多字节数据缓存完毕,再发送Execute Write Request执行写请求。其示例如下:
特征值通知
服务端执行Handle Value Notification,参数为特征值句柄和通知数据,向客户端推送通知。执行通知前,该特征值需要已经使能通知,并且将通知数据写入该特征值。该命令无需客户端返回响应。其示例如下:
特征值指示
与特征值通知类似,但是需要响应。服务端执行Handle Value Indication,参数为特征值句柄和指示数据,向客户端推送指示。执行指示前,该特征值需要已经使能指示,并且将指示数据写入该特征值。该命令需要客户端返回响应Handle Value Confirmation。其示例如下:
读写描述符
读写特征描述符分为四个子功能:读描述符,读长包描述符,写描述符,写长包描述符。
-
读描述符
读描述符同读特征值一致,其示例如下:
-
读长包描述符
读长包描述符与读长包特征值一致,其示例如下:
-
写描述符
写描述符与写特征值一致,其示例如下:
-
写长包描述符
写长包描述符与写长包特征值一致,其示例如下:
BLE安全机制
安全管理
设备间要安全通信,最重要的就是双方要共享一把密钥,在BLE就是LTK(Long Term Key)。Security Manager安全管理定义了一系列复杂的机制来完成LTK的实现:配对和密钥分发方法,这些方法的一套协议SMP,以及密码工具箱等。SM是利用密钥分发方式去完成通信的身份认证和加密功能。该安全架构设计的优点是:响应设备(responding device)对内存和处理能力的要求比初始化设备(initiating device)低。
这里要先讲下配对和绑定的关系。配对包括配对能力交换,设备认证,密钥生成,连接加密以及机密信息分发等过程。绑定是配对过程中会生成一个长期密钥(LTK,long-term Key),如果配对双方把这个LTK存储起来,那么这两个设备再次重连的时候,就可以跳过配对流程,而直接使用LTK对蓝牙连接进行加密。这就类似于TLS通信协议,配对类似握手过程,绑定类似会话恢复功能。
广义上的配对(Paring)包括绑定(Bonding)过程,只paring不bonding的应用情况非常少见。因此我们就拿配对paring当成paring和bonding两者的组合。
配对包括三个阶段,第一二阶段必须,第三阶段可选,如下所示:
阶段1:配对特性交换,交换各自支持的哪些配对特性,如是否支持LESC,是否支持MITM,是否支持OOB,以及输入输出能力等
阶段2:有两种情况:
- LE legacy pairing:STK(Short Term Key)短期密钥生成
- LE Secure Connections:LTK(Long Term Key)长期密钥生成
阶段3:传输特定密钥分发,Legacy paring需要分发LTK,IRK等,而SC paring只需分发IRK。
参考:低功耗蓝牙配对绑定解读和实践
阶段1:配对特性交换
配对总是由主机发起,从机进行响应,但是从机可以先发送Security_Request安全请求,因为如果主从以前配对绑定过,可以直接走加密流程,省去配对过程。
-
Security_Request
从机设备可以向主机发送安全请求命令,主机收到该命令后,要么加密链路,要么开始进行配对流程,要么拒绝该请求。比如主从设备如果第一次连接,主机收到Security Request后进入配对流程,配对成功后用长期密钥进行加密;如果之前主从设备配对过,有LTK,那么主机直接可用长期密钥进行加密。
Security Request命令包的格式如下:
-
Pairing_Request
主机向从机发送配对请求Pairing_Request命令,用于配对特性交换,配对请求Pairing_Request包的格式如下:
-
Pairing_Response
如果允许配对,从机须向主机回复Pairing_Response命令,完成配对特性交换,配对响应Pairing_Response包的格式如下:
可以看出,配对特性交换主要是交换:IO能力,OOB认证标志,认证请求,密钥最大长度,初始化者的密钥分发,响应者密钥分发。
-
IO Capability
IO能力是指设备的输入输出能力是怎样的,比如是否有键盘,显示屏等等,其综合能力表如下所示:
相应的IO能力值(一个字节)的定义如下:
其中,DisplayOnly:设备只有显示的输出能力(比如显示屏);DisplayYesNo:设备既有显示的输出能力,还具备键入Yes或No的输入能力(比如两个按键);KeyboardOnly:设备只有键盘的输入能力;NoInputNoPut:设备没有任何输入输出能力;KeyboardDisplay:设备既有键盘的的输入能力,也有显示的输出能力。
-
OOB data flag
带外数据标志表示设备是否可以用带外认证数据去认证对端设备。值定义(一个字节)如下:
-
AuthReq
认证请求是指明用于STK、LTK以及GAP绑定信息的安全特性Security Properties,SM定义的安全特性Security Properties分为四类:
- LE Secure Connection pairing:LE安全连接配对
- Authenticated MITM protection:可靠的中间人保护
- UnAuthenticated no MITM protection:不可靠的中间人保护
- No security requirements:无安全需求
这里的中间人是指第三方蓝牙设备,中间人保护是指设备双方交换参数时,第三方设备无法知道。认证请求标志(1字节)按位定义,包括绑定标志,启用中间人保护,使能SC配对,启用输入密码。如下:
-
密钥最大长度
该值(1字节)定义设备支持的密钥最大长度,密钥长度为7-16字节。
-
发起者密钥分发
该值(1字节)定义了发起者密钥分发的类型。以LE Secure Connections为例,从机向主机可分发的密钥类型有:
- 使用身份信息命令分发的IRK
- 使用唯一地址信息命令的公有设备或者随机设备地址
- 使用签名信息命令的CSRK
主机向从机可分发的密钥类型与上一致:
- 使用身份信息命令分发的IRK
- 使用唯一地址信息命令的公有设备或者随机设备地址
- 使用签名信息命令的CSRK
该值定义的密钥分发类型的格式如下:
其中:
- EncKey只针对LE legacy pairing模式,表示设备是否应当分发LTK;对于SC模式,该值被忽略
- IdKey表示设备是否应当分发IRK
- Sign表示设备是否应当分发CSRK
- LinkKey表示设备是否从LTK中派生出Link Key
-
响应者密钥分发
该值(1字节)定义了响应者密钥分发的类型。
对于发起者密钥分发和响应者密钥分发,总结如下:
- 如果是作为从机,Initiator Key Distribution意味着你期望主机分发哪种类型的密钥给你;Responder Key Distribution意味着你能分发哪种类型的密钥给主机。
- 如果是作为主机,Initiator Key Distribution意味着你能分发哪种类型的密钥给从机;Responder Key Distribution意味着你期望从机分发哪种类型的密钥给你。
阶段2:密钥生成
第一阶段交换的信息将用于选择第二阶段的密钥生成方法。密钥生成阶段有如下认证方式:
-
Just Works
仅工作方式。比如当主从设备双方IO能力均是NoInputNoOutput,即采用此方式
-
Numeric Comparison(只LE Secure Connections Pairing独有)
数字比较,显示6位的数字在显示器上,用户判断确定是否一致。比如主从设备双方IO能力均是DisplayYesNo,即采用此方式,主从设备显示6位数字,用户输入Yes或No确认这两个数字是否相等。
-
Passkey Entry
密码输入,用户在键盘中输入的一串数字,以达到相互认证的目的。比如从机设备IO能力为DisplayOnly,主机设备是KeyboardOnly,即采用此方式,从机显示一串数字,主机用键盘输入该数字。
-
Out of Band
根据不同的IO能力,密钥生成方法如下表:
接下来以设备双方都支持LE Secure Connections Pairing为例进行介绍。
-
LESC配对使用的ECDH密钥协商算法,采用P-256椭圆曲线。ECDH算法首先各自生成公私钥对,接着双方交换各自的公钥,最后根据ECDH算法计算出一把对称密钥DHKey,该密钥主从设备计算出来是一样的(为什么不能立马用该DHKey密钥呢?因为ECDH交换公钥的过程中,MITM中间人可以进行攻击,替换成自己的公钥,因此需要保证DHKey是真实的双方协商出来的,而不是与中间人协商出来的密钥,这就是接下来的认证阶段)。密钥协商阶段流程如下:
配对公钥交换命令的格式为:
-
接着按照第一阶段交换的信息,可以有以下方式完成认证:
Numeric Comparison方式
如果设备双方具备DisplayYesNo or KeyboardDisplay(即显示Yes和No或者键盘显示)能力,并且第一阶段MITM位为1的话,配对将会采取数字比较方式。认证流程如下:
主机和从机分别生成随机数Na和Nb,然后从机使用f4函数计算Cb认证值,计算如下
C b = f 4 ( P K b , P K a , N b , 0 ) Cb=f4(PKb, PKa, Nb, 0) Cb=f4(PKb,PKa,Nb,0)其中f4函数如下:
f 4 ( U , V , X , Z ) = A E S − C M A C X ( U ∣ ∣ V ∣ ∣ Z ) f4(U, V, X, Z) = AES-CMAC_X (U || V || Z) f4(U,V,X,Z)=AES−CMACX(U∣∣V∣∣Z)
可以看出其实就是计算密码学中的消息认证码,方法为AES_CMAC,密钥为生成的随机数Nb,消息为主从机双方的公钥和零值。接着从机将Cb发送给主机,主机收到Cb后则发送随机数Na,从机收到Na后发送随机数Nb给主机。主机收到Nb,用f4函数做同样的消息认证码计算,如果收到的Cb值与计算值一样,则继续往下走,否则终止配对,这一步可以保证Nb没有被篡改。
接下来,主机和从机分别用g2函数计算出Va和Vb,计算如下
V a = g 2 ( P K a , P K b , N a , N b ) Va=g2(PKa, PKb, Na, Nb) Va=g2(PKa,PKb,Na,Nb)
V b = g 2 ( P K a , P K b , N a , N b ) Vb=g2(PKa, PKb, Na, Nb) Vb=g2(PKa,PKb,Na,Nb)
其中g2函数如下:
g 2 ( U , V , X , Y ) = A E S − C M A C X ( U ∣ ∣ V ∣ ∣ Y ) m o d 2 32 g2(U, V, X, Y) = AES-CMAC_X (U || V || Y) mod 2^{32} g2(U,V,X,Y)=AES−CMACX(U∣∣V∣∣Y)mod232
其实也是计算消息认证码的方式,只不过最后要进行模2^32运算。然后将计算出Va和Vb结果转换成10进制,取最低的6位。比如如果Va或者Vb为0x 01 2e b7 2a
,则对应的十进制为19838762
,展示的6位值为838726
。
最后主机和从机分别展示计算出的Va和Vb值,交由用户去判断这个值是否一致,如果相同则认证成功,否则失败。
在这里,我有几个疑问:
a. 为什么需要生成随机数呢?
我看文档规范里说的随机数是用来防止重放攻击,不明白重放攻击什么时候会发生?
b. 不用随机数,使用哈希运算直接计算Va和Vb不更高效吗?
这个过程其实就是为了完成认证,即对主机A而言,自己在密钥协商阶段接收的对端公钥就是从机B的,而不是中间人C的;反之也一样,对从机B而言,自己在密钥协商阶段接收的对端公钥就是主机A的,而不是中间人C的。哈希运算只能保证完整性,无法认证。而消息验证码既可以保证完整性,也能进行认证,其是通过设备双方共享的对称密钥来实现的。那么这个密钥来自何处呢?有人可能会说用密钥协商出来的DHKey,这不回环了嘛,本来目的就是为了保证DHKey是真实设备双方协商出来的。因此这个认证阶段最重要的就是如何安全地共享一把用于消息认证码计算的密钥呢?这就可以回答问题c了。
c. 如何保证密钥协商阶段的公钥就是真实的对方呢?
我们假设主机A和从机B在密钥协商阶段遭受了中间人M攻击,其可以获取AB之间的通信包并进行拦截篡改,即与A通信时冒充B,与B通信时冒充A。这样主从机AB交换公钥时,其实际上收到的是中间人M的公钥,因此最后AB协商出来的DHKey其实是它们分别与中间人M的对称密钥,而且两把密钥不一致。这些只有中间人M明白,主从机AB完全不知道,还以为是在与真实的对方通信。接下来有点难懂,还是尽量讲清楚。
设备B在3c步骤计算Cb时,其实使用的PKa公钥其实是中间人M的。为了保证6a步骤的验证顺利通过,中间人必须同时篡改Cb和Nb数据。因为:如果Cb和Nb都不篡改,那么设备A在6a计算Cb时,其输入的公钥PKb其实是以前中间人M的,而设备B计算时输入的PKb是自己的,这样Cb计算值和收到的值不等,导致验证不过;如果只更改Cb,中间人不知道改成多少,因为Nb是在Cb之后发送的,其不能提前预知Nb的值,这样只改Cb,而不改Nb,6a验证步骤无法通过;同样只改Nb,而不改Cb,也会导致验证失败。
因此为了保证6a步骤验证顺利通过,中间人需要自己生成一个Nb值,并且在计算Cb时,将PKb公钥替换成自己的公钥,PKa公钥还是真实的Pka公钥,这样6a步骤就能顺利通过。但是到第7阶段,7a步骤计算的值Va=g2(PKa, PKm, Na, Nbm),7b步骤计算的值Vb=g2(PKm, PKb, Na, Nb),很明显Na密钥相同,但是其他消息输入参数不一致,Va不等于Vb,用户会校验失败。可能有人会说把Na也改了,中间人在更改Na之前提前计算出设备A计算的Va值,然后反推需要把Na改成多少才会让设备B计算的Vb值与Va一样。即此时是这样的情况:Va=g2(PKa, PKm, Na, Nbm),Vb=g2(PKm, PKb, Nam, Nb)。这样概率上有可能的,但是观察g2函数的表达式,可以发现有一个mod2^32
动作,根据Va的值反推以前的消息值在数学上是很困难的(即使能够反推也需要类似超级计算机计算很长的时间),因此如果Na的值无法改成意想的值,用户也无法校验成功。
这个阶段涉及的认证值交换和随机数交换命令格式分别如下:
总结一下,可以看出公钥真实的保证性其实交给了用户,因为用户可以看到Va和Vb值是否相等。
Just Works方式
如果可以保证主从配对过程中不会存在中间人攻击,可以使用Just Works方式,Just Works方式不会有7a和7b步骤,也即不会展示6位