通用访问规范GAP(Generic Access Profile)是BLE设备内部功能对外的接口层,它规定了三个方面:GAP角色、模式和规程、安全问题。

GAP层将设备分为四种角色,分别是外围设备,中央设备,播报设备和观察设备。这些设备围绕着广播和连接的差异性而区分,外围设备和播报设备对外发出广播数据,中央设备和观察设备扫描外部广播数据,播报设备和观察设备通常不建立连接,而外围设备和中央设备可以建立连接。

围绕着广播和连接,GAP层定义了许多不同的广播模式和连接模式,在不同模式下的操作称为“规程”。

对于安全问题,GAP层提供了BLE安全管理器的一些列参数设置接口,包括:设备的安全模式、IO能力、安全级别和密钥长度等参数。设备需要根据实际能力设置GAP的安全管理器参数,从而使用合适的配对方法。

GAP层可以与L2CAP建立联系,设置自定义的MTU值。

1. GAP角色

GAP层定义了四种角色:

  • 外围设备(Peripheral)
  • 中央设备(Central)
  • 播报设备(Broadcaster)
  • 观察设备(Observer)

外围设备可以发送广播数据和接收连接请求,进而建立连接,对应着链路层的广播状态。

中央设备扫描广播数据和发送连接请求,进而建立连接,对应着链路层的扫描状态或发起状态。

播报设备可以发送广播和接收扫描请求,通常不建立连接,对应着链路层的广播状态。

观察设备可以扫描广播和发起扫描请求,通常不建立连接,对应着链路层的扫描状态。

由于链路层支持同时拥有多个状态机,GAP层也支持一个设备同时具有多个GAP角色,比如在一个连接中充当中央设备,同时对外发出广播充当外围设备。

2.用户接口

GAP定义了几个与用户操作密切相关的参数:设备地址,设备名,PIN码和设备外观。

2.1 设备地址

设备地址在协议栈内部指BD_ADDR,在用户界面显示为“Bluetooth Device Address”。

设备地址为一个6字节的整形数组成,可以用冒号作为分隔符,比如00:0C:3E:3A:4B:69。

在用户界面,设备地址以自然顺序显示,而内部的BD_ADDR则以逆序保存,对于上述地址,BD_ADDR[0]等于0x69而不是0x00。

设备地址分为共有地址和随机地址,随机地址分为静态随机地址和私有地址,私有地址进一步分为可解析的私有地址和不可解析的私有地址。常用的地址为共有地址和可解析的私有地址两种类型。

关于设备地址类型分析,参见链路层的文章介绍。

2.2 设备名

设备名称仅起识别设备的作用,在用户界面显示为“Bluetooth Device Name”。

设备名最长可达248个字节,但是对端设备可能并不能显示这么长的名称。

设备名支持UTF-8编码,因此设备名可以使用中文。

2.3 PIN码

PIN码指两个设备配对时使用的passkey密码,在用户界面显示为“Bluetooth Passkey”。

PIN码为6位十进制整形数,因此它的有效范围为000000-999999(0x00000000 – 0x000F423F)。使用时必须显示全部6位数字,包括前导0。

2.4 设备外观

设备外观仅起识辅助别设备的作用,在用户界面显示为一个图标或一个字符串。

设备外观为一个2字节数,扫描设备可以通过设备外观值为设备分配一个合适的图标或描述。

3. 模式和规程

模式表示一种工作状态,规程是针对模式实现的一套操作方法。模式和规程成对出现,GAP规定了五种模式和规程,如下:

模式 规程
Broadcast Mode Observer Procedure
Discovery Mode Discovery Procedure
Connection Mode Connection Procedure
Bonding Mode Bonding Procedure
Periodic Advertising Mode Periodic Advertising Procedure

3.1 播报模式和观察规程

播报模式并不等同于普通的广播状态。

在播报模式下,设备在广播事件中发送不可连接的广播数据,播报设备可以响应外部的扫描请求。

播报设备的广播数据格式与普通的广播数据相同,但是它不设置LE Limited Discoverable Mode和LE General Discoverable Mode这两个标志位,即播报设备是Non-discovery设备,这意味着中央设备扫描到该广播数据,应该选择忽略。

观察规程用于监听播报设备的广播数据和扫描响应数据,也可以监听普通广播设备。

3.2 可发现模式和发现规程

可发现模式分为三种,如下:

可发现模式 描述
Non-Discoverable mode 不可发现模式,扫描设备应该选择忽略这类广播数据。
Limited Discoverable mode 有限发现模式,广播数据仅工作有限时间内是可被发现的。
General Discoverable mode 普通发现模式,没有额外限制。

有限发现模式将广播数据的LE Limited Discoverable Mode位置为1,普通发现模式将广播数据的LE General Discoverable Mode位置为1,如果这两个标志位均不设置,就是不可发现模式。

不可发现模式的广播数据与其他两种模式相同,所以其广播数据仍然能够被扫描设备正确读取,但由于没有设置相应的标志位,扫描设备在解析广播数据时应该尊重其不愿意被发现的意图,主动忽略该广播数据。

使用观察规程的观察设备,则不会忽略不可发现模式的广播数据。

有限发现模式通常用于用户指定的行为让设备临时进入可发现状态,可发现状态持续时间为T_GAP[lim_adv_timeout]。

普通发现模式是默认模式,它没有时间限制。

发现规程分为两种,如下:

发现规程 描述
Limited Discovery Procedure 有限发现规程,仅能发现有限发现模式的广播数据。
General Discovery Procedure 普通发现规程,没有额外限制。

有限发现规程,仅处理有限发现模式下的广播数据,包括设备地址和广播数据,忽略其他发现模式下的广播设备。

常规发现规程,能普通发现模式和有限发现模式下的广播数据。

此外还有一种发现规程,专用于发现设备名称,如下:

发现规程 描述
Name Discovery Procedure 设备名发现规程,用于发现广播设备的设备名称。

设备名发现规程,可以发现普通发现模式和有限发现模式下的广播设备名称。

发现设备名称的步骤如下:

  1. 建立连接
  2. 读取GATT中的名字特征值

3.3 可连接模式和连接规程

可连接模式分三种,如下:

可连接模式 描述
Non-Connectable Mode 不可连接模式,无法与其他设备建立连接。
Directed Connectable Mode 定向可连接模式,可以与指定的中央设备建立连接。
Undirected Connectable Mode 非定向可连接模式,可以与任何中央设备建立连接,这是默认的可连接模式。

而相关的连接规程则由四种,如下:

连接规程 描述
Auto Connection Establishment Procedure 自动连接建立规程,利用中央设备的设备地址白名单,一旦地址匹配就自动建立连接。
General Connection Establishment Procedure 普通连接建立规程,这是默认的连接规程,没有额外条件。
Selective Connection Establishment Procedure 可选连接建立规程,利用中央设备的设备地址白名单,只有地址匹配的设备才能建立连接。
Direct Connection Establishment Procedure 定向连接建立规程,与指定地址的外围设备建立连接。

此外,还有两个与连接相关的规程,如下:

规程 描述
Connection Parameter Update Procedure 连接参数更新规程,更新连接参数信息。
Terminate Connection Procedure 终止连接规程,终止当前连接。

3.4 可绑定模式和绑定规程

可绑定模式分为:

可绑定模式 描述
Non-Bondable mode 不可绑定模式,设备不支持配对操作,在配对请求命令中清除Bonding_Flags标志位。
Bondable mode 可绑定模式,设备将设置认证请求命令中的Bonding_Flags标志位,并且保存绑定信息。

两个未绑定的设备,在访问需要绑定权限的数据时,执行绑定规程。

3.5 周期广播模式和周期广播规程

周期广播模式分为:

模式 描述
Periodic Advertising Synchronizability mode 周期广播同步模式,发送周期广播事件的同步信息,适用于播报设备,
Periodic Advertising mode 周期广播模式,发送周期广播数据,适用于播报设备

周期广播规程为:

规程 描述
Periodic Advertising Synchronization Establishment procedure 周期广播同步建立规程,接收周期广播事件的同步信息并同步周期广播事件,适用于观察设备。

3.6 安全模式和认证规程

共有两种安全模式:

安全模式 描述
LE Security mode 1 安全模式1,使用认证信息保证安全。
LE Security mode 2 安全模式2,使用数字签名保证安全。

安全模式1下有四种安全级别:

  1. No security (No authentication and no encryption)
  2. Unauthenticated pairing with encryption
  3. Authenticated pairing with encryption
  4. Authenticated LE Secure Connections

四种安全级别围绕着认证和加密进行,安全级别依次增加,第1种安全级别没有认证和加密, 第2种安全基本提供未认证的加密,第3、4种安全级别能够提供认证和加密。

安全模式2下有两种安全级别:

  1. Unauthenticated pairing with data signing
  2. Authenticated pairing with data signing

假如设备同时要求加密和数字签名,将视认证需求选择合适的安全模式,比如需要认证则选择模式1.3,不需要认证则选择模式1.2,如果需要安全连接则选择模式1.4。

共有四种安全规程,如下:

规程 描述 适用安全模式
Authentication procedure 认证规程,执行认证和加密操作。 安全模式1
Authorization procedure 授权规程,用户行为确认是否为某个操作提供授权。 安全模式1
Connection data signing procedure 连接数据签名规程,在未加密的连接中传输认证的数据。 安全模式2
Authenticate signed data procedure 认证已签名的数据规程,校验带有前面的数据是否有效。 安全模式2
Encryption procedure 加密规程,对连接和数据进行加密。 安全模式1

3.7 隐私规程

隐私与私有地址有密切关系,跟私有地址相关的规程如下:

规程 描述
Non-resolvable private address generation procedure 不可解析私有地址生成规程
Resolvable private address generation procedure 可解析私有地址生成规程
Resolvable private address resolution procedure 可解析私有地址解析规程

4. 广播包

广播包和扫描响应使用相同的数据格式,如下:

GAP_Adv_Packet_Format

一个广播包由多个AD Structure组成,传统广播包的最大长度为31字节,扩展广播包的最大长度为255字节,未占用的数据则补零。

一个AD Structure中包含三个元素:长度、广播数据类型和广播数据。

其中长度指广播数据类型加上广播数据的总长度,广播数据类型决定了广播数据的属性,可以代表设备名、设备地址或服务的UUID。完整的广播数据类型可以在官方网站检索(链接)。

动态的广播数据适合放在广播包中发送,静态的广播数据适合放在扫描响应包中发送。

7. GAP特征项

每个BLE设备的GATT均包含必要的GAP服务项,GAP服务项包含以下特征项:

特征项 UUID 描述
Device Name 0x2A00 读取设备名称
Appearance 0x2A01 读取设备外观
Peripheral Preferred Connection Parameters 0x2A04 读取期望的连接参数
Central Address Resolution 0x2AA6 中央设备支持解析地址,供外围设备读取以确定中央设备能否使用地址解析,仅在使能了隐私功能时使用,否则应删除
Resolvable Private Address Only 0x2AC9 设备仅使用可解析的随机地址,供对端设备读取以确定该设备在绑定后是否仅使用可解析的随机地址,仅在使能了隐私功能时使用,否则应删除

(完)

安全管理(Security Manager)定义了设备间的配对过程。

配对过程包括了配对信息交换、生成密钥和交换密钥三个步骤。具有不同的输入输出能力的设备将采用不同的配对方式,两个设备完成配对将加密连接,产生LTK、IRK、CSRK等密钥,这些密钥将支持加密、隐私、签名等安全特性。

安全管理协议定义了配对相关的数据结构。

安全管理数据都通过L2CAP的安全管理信道传输,安全管理协议通过GAP层暴露用户接口,由用户设置设备的输入输出能力和配对参数。

1. 配对概述

BLE 4.2协议新增了一种配对方法,称为“LE安全连接配对”,新的配对方法增加了安全性,为了与BLE 4.1及以前的配对方法做区别,之前的配对方法统称为“传统配对”。

传统配对和LE安全连接配对过程基本一致,都分为三个步骤,仅第二步骤生成密钥上有所不同。

三个步骤为:

  1. 交换配对信息,确定配对模式
  2. 执行配对模式,生成密钥
  3. 分发密钥,保存密钥

三个步骤具有明确的时序关系,并且前一个步骤将显著影响下一个步骤,如下所示:

SM_Pairing

两端设备先建立连接,然后才能进行配对操作。配对无需在连接后立即执行,可以在任何需要时候进行。

2. 配对信息

配对信息包括:

  • 认证需求
  • IO 能力
  • OOB
  • 密钥长度
  • 是否绑定

2.1 安全特性

安全特性取决于设备的认证需求,可选的安全特性如下:

安全特性 MITM保护 所属配对方法
LE Secure Connections pairing Yes LE安全连接配对
Authenticated MITM protection Yes 传统配对
Unauthenticated no MITM protection No 传统配对
No security requirements No 传统配对

MITM(Man in the Middle)指中间人攻击,假如第三方设备攻破了BLE连接,A设备发送的消息被C设备接收,C设备再转发给B设备,A与B设备相互以为建立了连接,而实际上所有的数据通信都经过了C设备转发。

前两种安全特性可以实现MITM保护,后两种则无法防护MITM攻击。

四种安全等级从上至下安全性依次降低。

安全特性信息会持久保存在设备的安全数据库中。

2.2 IO能力

一个设备具有的输入输出能力分为以下几种情况:

输入能力 描述 输出能力 描述
No input 无输入 No ouput 无输出
Yes/No 仅能输入是或否 Numeric output 能显示数字
Keyboard 输入数字以及确认

不同的输入输出能力,将组合出不同的IO能力,如下:

No output Numeric output
No input NoInputNoOutput DisplayOnly
Yes/No NoInputNoOutput DisplayYesNo
Keyboard KeyboardOnly KeyboardDisplay

2.3 OOB能力

OOB(Out of Band)指利用NFC或Wifi等非BLE通信方式传递密钥,它要求设备具有OOB接口能力。

传统配对方式中,要求两端设备都设定了OOB标志位,在LE安全连接配对方式中,只需一个设备设定了OOB标志位即可。

2.4 密钥长度

密钥长度决定了加密强度,越长的密钥其加密强度越高,但加解密所消耗资源和时间也越多。

密钥长度有效值为:7-16字节。

当两端设备的密钥长度值不同,取较小值为有效值。

3. 配对模式

可选的配对模式包括:

  • Just Works
  • Numeric Comparison
  • Passkey Entry
  • Out Of Band (OOB)

3.1 选择模式

如果两端设备均选择No-MITM protection安全特性,则使用Just Works配对模式;如果两端设备(对于LE安全连接配对,仅需要一个设备)均选择OOB,则使用OOB配对模式;否则根据两端设备的IO能力选择配对模式。

两端设备的IO能力中,如果有一端设备是NoInputNoOutput,则只能使用Just Works配对方式,其他的IO能力组合所对应的配对方式如下:

DisplayOnly DisplayYesNo KeyboardOnly KeyboardDisplay
DisplayOnly Just Works Just Works Passkey Entry Passkey Entry
DisplayYesNo Just Works Just Works, Numeric Comparison Passkey Entry Passkey Entry, Numeric Comparison
KeyboardOnly Passkey Entry Passkey Entry Passkey Entry Passkey Entry
KeyboardDisplay Passkey Entry Just Works, Numeric Comparison Passkey Entry Passkey Entry, Numeric Comparison

如果采用了Just Works方式,一定是未认证的,Passkey Entry和Numeric Comparison方式则是认证的。

3.2 Just Works

Just Works配对模式不能够防护窃听和MITM威胁。如果设备的配对过程不被窃听,配对结束后连接被加密并且保证安全。

所以Just Works虽然不如其他配对方式安全,仍然比不配对的连接要安全。

Just Works通常用于一端设备完全没有输入输出能力的场景,所以它将临时密钥TK设置为固定值0,这样两端设备可以根据这个TK值进行后续的加密操作。

3.3 Passkey Entry

Passkey Entry配对模式可以防护MITM威胁,有限的防护窃听。如果设备的配对过程不被窃听,配对结束后连接被加密并且保证安全。

Passkey Entry方法在一端设备上显示一个随机的6位十进制数作为密码,另一端设备输入该密码。passkey作为TK的初值进行后续加密运算,进而可以通过比较加密运算的中间值来判断输入的密码与显示的密码是否一致。比如passkey=001024(400h),则TK=0x00000000000000000000000000000400。

3.4 OOB

OOB(Out of Band)配对模式使用非BLE协议传输TK。用户通过键盘输入Passkey Entry,也属于一种OOB传输TK。

OOB传输通道的安全性决定了配对过程的安全性。

3.5 Numeric Comparison

数值比较仅能用于LE安全连接配对方法,它能够防护MITM和窃听威胁。

数值比较配对模式在两端设备分别显示一串数字,用户比较数字是否相等并通过输入Yes/No来确认,从而实现认证。

这种模式仅需要两个按键即可完成输入,适合用在小型设备上。

3.6 安全性

BLE通信面临的外部威胁有两类:被动威胁和主动威胁。

被动威胁指第三方设备监听配对过程中的密钥数据,有了密钥即可解密后续的连接数据。

主动威胁指MITM,通过伪造身份参与到通信连接中。

一旦BLE的连接经过了认证,即可抵挡MITM威胁,经过认证的设备,就意味着对端设备不是一个伪造偷听设备。

传统的配对方法使用临时密钥TK作为加密运算的初值,而交换TK时可以被窃听设备获取,从而破解后续的加密措施。

不同的配对方法,其安全防护能力如下:

安全性 传统配对 LE安全连接配对
MITM防护 Passkey Entry, OOB Numeric Comparison, Passkey Entry, OOB
窃听防护 OOB 全部

4. 生成密钥

4.1 传统配对

传统配对可以使用Just Works,Passkey Entry和OOB三种算法,配对成功后生成短期密钥STK(Short Term Key)。

生成STK的算法如下:

STK = s1(TK, Srand, Mrand)

其中s1算法是专门用于生成STK的加密算法,TK值可以通过不同的配对模式获得(Just Works模式下TK=0,Passkey Entry模式下TK=passkey,OOB模式下TK=oob input),Mrand和Srand分别为主机和从机生成的128位的随机数。

在两端设备生成STK之前,还需要确认对端设备的安全性,称为“认证”。

配对发起端设备生成一个Mrand随机数,然后根据TK值以及设备配对信息和地址信息等,生成一个确认值Mconfirm。配对响应端设备也按照相同的步骤生成一个Srand和Sconfirm。

然后两端设备交换各自的随机数和确认值。

响应端根据发起端的随机数Mrand按照相同的算法计算出一个新的Mconfirm_new,比较Mconfirm和Mconfirm_new,如果二者匹配,说明发起端设备是安全的。

发起端根据发起端的随机数Srand计算出新的Sconfirm_new,比较Sconfirm和Sconfirm_new,如果二者匹配,说明响应端设备是安全的。

然后两端设备命令控制器加密连接,并根据Srand和Mrand生成STK值。

一旦产生了STK,即说明两端设备之间的连接已经加密和认证。

4.2 LE安全连接配对

安全连接配对可以使用全部四种配对模式,配对成功后生成长期密钥LTK(Long Term Key)。

安全连接配对模式使用椭圆曲线加密算法ECDH( Elliptic Curve Diffie-Hellman)来解决TK被窃听的威胁。

ECDH算法具有数学不可逆的特点。对于一个ECDH运算Q=Pk,如果P和k是已知,计算出Q很容易,但是反过来如果P和Q是已知,计算出k则很难。在密钥交换时,设置一对“公钥-私钥”对,私钥是上式子中的k,公钥是Q,加密算法为P,交换密钥时仅交换公钥Q,私钥永远不对外暴露,攻击者即使获得了公钥也无法反推出私钥。(参考

引入ECDH算法是安全连接配对与传统配对最大的区别。

生成LTK需要三个步骤:

  1. 交换公钥,生成ECDH密钥
  2. 认证设备
  3. 利用私钥生成LTK

在配对开始前,每个设备先准备自己的公钥和私钥对,公钥可以对外传输,私钥不对外传输。

第一步交换攻击,并利用公钥和私钥生成ECDH密钥,这个ECDH密钥将用来生成LTK,由于私钥和ECDH密钥不对外传输,因此可以保证不被窃听。

第二步认证对端设备,具体过程与传统配对基本一致,即在设备内根据随机数生成一个确认值,再将随机数和确认值穿给对端设备,对端设备根据随机数重新生成一个确认值,并与收到的确认值作比较,符合则认证通过,不符合则认证失败。

如果使用Just Works配对模式,无需用户输入,两端设备自动完成随机数和确认值的交换和校验过程,完成认证。

如果使用Numeric Comparison配对模式,无需用户输入,两端设备自动完成随机数和确认值的交换过程,并将最终确认值显示出来等待用户确认,如果确认通过则完成认证。

如果使用Passkey Entry配对模式,需要用户输入passkey作为初值,然后两端设备自动完成随机数和确认值的交换和校验过程,完成认证。

如果使用OOB配对模式,不强制要求双向OOB通信,仅单向OOB通信即可完成认证。利用OOB通道传输随机数,保证随机数不被窃听。

第三步生成LTK,仍然要执行一次确认值校验操作。

5. 分发密钥

BLE工作时可能用到以下几种密钥:

密钥 描述 适用范围
IRK (Identity Resolving Key) 身份识别密钥,用于解析私有地址 传统配对、LE安全连接配对
CSRK (Connection Signature Resolving Key) 连接签名解析密钥,用于解析签名数据 传统配对、LE安全连接配对
LTK (Long Term Key) 长期密钥,用于解析加密连接。传统配对中,LTK是由STK进一步生成;LE安全连接配对中,LTK是配对结束后生成。 传统配对
EDIV (Encrypted Diversifier) 在传统配对中,用于生成LTK。它是绑定信息的一部分。 传统配对
Rand (Random Number) 在传统配对中,用于生成LTK。它是绑定信息的一部分。 传统配对

其中Rand用以生成EDIV,EDIV用以生成LTK。传统配对的第二步骤结束时,连接被STK加密以传输各种密钥,此时将STK临时当做LTK使用,配对成功以后,需要使用Rand、EDIV和LTK作为密钥来加密和解析连接。

加密过程在链路层进行,通过执行链路层的加密规程进行加密。查看LL_ENC_REQ命令,其输入参数包括Rand和EDIV。

密钥生成以后,两端设备将交换各自的密钥信息。传统配对设备可以交换以上全部五种密钥,LE安全连接配对仅交换IRK和CSRK。交换密钥时会保存对端设的设备地址,使设备地址与LTK关联在一起。

至此,配对过程全部结束。

6. 安全管理协议

安全管理协议定义配对过程中用到的各种数据格式和协议接口。

安全管理操作数据使用L2CAP的Security Manager信道,传统的配对方法使用默认的L2CAP MTU值23,LE安全连接配对方法将L2CAP MTU值扩大到65。

安全管理协议的操作以命令的形式进行,命令的格式如下:

字段 Code Data
长度 1 octets 0 – 22 or 64 octets

命令码Code代表了不同的命令类型,全部命令码如下:

Code Command
0x01 Pairing Request
0x02 Pairing Response
0x03 Pairing Confirm
0x04 Pairing Random
0x05 Pairing Failed
0x06 Encryption Information
0x07 Master Identification
0x08 Identity Information
0x09 Identity Address Information
0x0A Signing Information
0x0B Security Request
0x0C Pairing Public Key
0x0D Pairing DHKey Check
0x0E Pairing Keypress Notification

下面介绍配对请求的命令格式,配对响应与之完全相同。

发起端设备发起配对请求,执行配对信息交换(Pairing Feature Exchange)。

配对请求命令的结构如下:

SM_Pair_Request

  • IO Capability表示不同的IO能力
  • OOB data flag表示是否具有OOB能力
  • AuthReq表示认证请求,它的结构如下:
字段 Bonding_Flags MITM SC Keypress CT2 RFU
长度 2 bits 1 bit 1 bit 1 bit 1 bit 2 bits

其中Bonding_Flags表示是否保存绑定信息,MITM是否要求MITM防护能力,即是否需要认证,SC(Secure Connection)表示是否使用LE安全连接配对,Keypress为KeyboardOnly设备提供一些必要的输入状态信息,CT2与经典蓝牙有关。

  • Max Encryption Key Size表示最大密钥长度,有效范围为7-16字节。
  • Initiator Key Distribution表示发起者需要分发的密钥。
  • Responder Key Distribution表示响应者需要分发的密钥。

(完)

通用属性规范GATT(Generic Attribute Profile)将ATT层定义的属性打包成不同的属性实体,包括服务项、特征项和描述符,这些属性实体组合在一起组成规范,即GATT规范。GATT规范是服务项的集合,服务项是特征项的集合,特征项携带了属性参数和数据,描述符协助特征项描述特征值的形式和功能。

GATT层按照命令的传输方向将设备分成GATT客户端和GATT服务端。客户端发起命令,服务端发出数据。GATT规范定义了客户端设备发现服务端设备的服务项的方法,建立连接以后,客户端设备可以通过发现方法检索服务端设备的GATT服务项和特征项,进而发送命了或数据。

服务端向客户端发送数据以通知和指示的形式发送,客户端收到指示信息需要返回确认信息。

服务端可以向客户端发送通知和指示,客户端按需返回响应。

1. 属性

1.1 GATT角色

客户端:设备发起命令、请求并接受响应、通知和指示。

服务端:设备接收命令、 请求并发出响应、通知和指示。

设备可以同时属于客户端和服务端。

GATT角色与执行过程相关,它不与设备绑定。设备在执行一个过程时,根据发起命令或接收命令而决定它是服务端还是客户端,该过程结束后就释放GATT角色。

GATT角色不与链路层的主机和从机角色绑定。一个链路层的主机,通常担任GATT客户端角色,也可以担任GATT服务端角色。

1.2 属性PDU

属性实体PDU如下:

字段 Attribute Handle Attribute Type Attribute Value Attribute Permissions
长度 2 octets 2 or 16 octets variable implementation specific

一个属性包含四个字段:属性句柄、属性类型、属性值和属性权限。

属性句柄用于指定具体的属性。属性句柄有效范围为0x0000-0xFFFF,属性句柄按步进1的增序排列,但有时可能会出现空缺。

属性类型为2字节、4字节或16字节的UUID。 如果是4字节UUID,在封装成属性PDU时根据蓝牙基础UUID转换成16字节标准UUID。

属性值字段包含了属性的具体数据。

属性权限决定了属性是否可读或可写。

1.3 属性协议PDU

两个设备属性层之间根据属性协议传输数据,属性协议包括几种类型:命令、请求、响应、通知、指示和确认。

属性协议PDU如下:

字段 Opcode Attribute Parameters Authentication Signature
长度 1 octet variable 12 octets

操作码Opcode决定了该PDU的操作过程类型。另外,操作码中包含一个认证标志位。

属性参数中包含了命令或请求的参数,或响应的数据。

最后字段的认证签名为可选字段,仅用于带签名的写操作,当操作码的认证标志位为1,则需要认证签名字段,否则不需要改字段。

1.4 属性缓存

客户端与服务端建立连接后,执行发现过程,以获取服务端所携带的全部属性。属性缓存功能用于保存服务端设备的属性句柄,使下一次重新连接时无需执行发现过程。

一般情况下,服务端设备的属性不会改变,但是执行固件升级则可以改变设备的属性。

如果改变设备的属性,将从Service Changed characteristic发出一个指示PDU,告知客户端设备服务端设备的属性发生了改变。该指示PDU中包含了发生改变的属性句柄范围,客户端设备收到该指示,重新执行发现过程,获取更新后的服务端设备的属性句柄。

如果设备的属性确定不能发生改变,则无需增加Service Changed characteristic属性。

如果两端设备完成绑定,则属性缓存信息一直有效,直到收到了Service Changed characteristic发出的指示。如果在服务端的属性在断开后发生了改变,则服务端在下次重连时候发送指示给客户端设备重新缓存属性句柄。

1.5 属性分组

GATT定义了三种属性分组:主要服务、次要服务和特征项。

一个属性分组包括声明和定义。

主要服务和次要服务可以使用“按组类型读取”请求获得,特征项不可以。

1.6 属性结构

蓝牙协议中包含了许多种GATT规范,每个规范适配一种用户案例,比如FindMe规范适配查找物件的场景,心率传感器规范适配心率测量场景。

每个规范均中均有若干服务项和特征项,服务项和特征项都属于属性实体,它们携带了通信中传输的数据。

服务项分为主要服务和次要服务,主要服务可以引用(Include)另一个主要服务或次要服务,客户端设备可以通过“主要服务发现过程”获取主要服务信息。

特征项包括一个声明、配置、数据和描述符。描述符用于描述特征项的数据如何被访问和展示。

规范、服务项和特征项之间有明确的包含关系,一个GATT规范中可以包括多个服务项,一个服务项中可以包括多个特征项。

GATT的规范结构框图如下:

GATT_Profile_Structure

2. 属性类型

属性的类型由UUID表示,协议栈预留了一些16-bit的UUID来表示常用的属性类型。

2.1 服务项

服务项必须包含一个服务项声明,可选地包含多个其他服务项和特征项。所包含的其他服务项和特征项均是该服务项的一部分。

服务项的声明格式如下:

GATT_Service_Declaration

服务项可以是主要服务项(UUID=0x2800)或次要服务项(UUID=0x2801)。

主要服务项可以独立使用,次要服务项一定要被其他服务项包含引用。

协议栈文档中对次要服务项的使用场景解释有限,在绝大多数情况下均可以不使用次要服务项,仅使用主要服务。

2.2 包含

包含现了一个引用机制,比如需要扩展一个现有的服务项,可以在新的服务项中引用该服务项。

假如服务项中包含了其他服务项,则需要加入包含的声明(UUID=0x2802)。

协议栈文档中对包含的使用场景解释有限,在绝大多数情况下均可以不使用包含功能。

2.2 特征项

特征项是GATT数据的载体。

特征项包括:特征项的声明(UUID=0x2803),特征值的声明,以及若干描述符。特征值也是一个属性,它的句柄和UUID在特征项的声明中给出。

特征项始于该特征项的声明,结束语下一个特征项的声明。

特征项的声明数据格式如下:

GATT_Characteristic_Format

其中属性值字段包括了特征值功能特性(Characteristic Properties),特征值的属性句柄和特征值的UUID。

特征值功能特性如下表所示:

特征值功能 描述
Broadcast 0x01 允许广播该特征值
Read 0x02 允许读该特征值
Write Without Response 0x04 允许写该特征值,不需要Response
Write 0x08 允许写该特征值,需要Response
Notify 0x10 允许该特征值发送通知
Indicate 0x20 允许该特征值发送指示
Authenticated Signed Writes 0x40 允许带认证签名的写该特征值
Extended Properties 0x80 扩展特性

其中,Broadcase(0x01)、Notify(0x10)和Indicate(0x20)要求该特征值具有服务端特征项配置描述符(CCCD)。

特征项的声明中属性字段的特征值UUID跟特征值的声明中的UUID一致。

特征值的声明中包含了特征值所携带的数据内容,其格式如下:

GATT_Characteristic_Value_Format

2.3 描述符

描述符也是一种属性,它是特征项的一部分,用以提供特征值的额外信息。协议栈定义了6种不同的描述符,如下:

属性类型 UUID 描述
«Characteristic Extended Properties» 0x2900 特征项的扩展描述符
«Characteristic User Description» 0x2901 特征项的用户描述符
«Client Characteristic Configuration» 0x2902 客户端特征项配置描述符
«Server Characteristic Configuration» 0x2903 服务端特征项配置描述符
«Characteristic 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字节、带符号整数、无符号整数、浮点数、字符串、结构体等。还可以指定数据的指数、单位、名字空间、描述信息等。

0x2905 聚合特征项格式描述符,专用于聚合特征项。所谓聚合特征值,是指多个特征值共同组合成一个数值,每个特征值仅是该聚合数值的一部分。

3. GATT功能

GATT规范实现了以下功能:

  1. Server Configuration
  2. Primary Service Discovery
  3. Relationship Discovery
  4. Characteristic Discovery
  5. Characteristic Descriptor Discovery
  6. Reading a Characteristic Value
  7. Writing a Characteristic Value
  8. Notification of a Characteristic Value
  9. Indication of a Characteristic Value
  10. Reading a Characteristic Descriptor
  11. Writing a Characteristic Descriptor

这些功能利用了“深入BLE协议栈 —— 属性协议”中的属性协议PDU一节中的多种读写属性PDU。

下面具体分析。

3.1 服务端配置

该功能呢包含一个子功能:交换两端设备的ATT_MTU。

客户端设备发送Exchange MTU Request,其中包含了该设备的ATT_MTU,服务端设备返回Exchange MTU Response,其中包含了该设备的ATT_MTU,取二者的较小值作为协商的ATT_MTU值。

3.2 发现主要服务项

该功能包含两个子功能:发现全部主要服务项,按UUID发现主要服务项。

发现全部主要服务项

该功能向服务端设备发送Read By Group Type Request,起始句柄为0x0001,结束句柄为0xFFFF,属性类型为0x2800(主要属性的UUID),查找全部符合条件的首要服务项。

服务端返回Read By Group Type Response,响应中包含多个属性信息组成的列表,单个属性信息包含三个参数:元素长度、属性组首尾句柄、属性的UUID。

因为该列表长度不能超过一个属性层的PDU长度,所以需要多次执行请求和响应,直到服务端设备返回“未找到属性项”或到达结束句柄,才能获取全部的主要服务项,如下图所示:

GATT_Discover_All_Primary_Services

观察上图,客户端第一次发起请求,查找主要服务项,首末句柄分别是0x0001和0xFFFF。

服务端返回响应中包含三个元素,每个元素代表一个首要服务项。每个元素的长度为0x06。第一个首要服务项的属性句柄为0x0001,类型为UUID1,末尾句柄为0x000F。第二个服务项的属性句柄为0x0010,类型为UUID2,末尾句柄为0x0017。第三个服务项的属性句柄为0x0100,类型为UUID3,末尾句柄为0x01FF。

客户端发起第二次请求,起始句柄设为0x2000。

服务端返回响应中仍然包含三个元素,每个元素的长度为0x06。三个元素分别表示三个首要服务项,其UUID分别为UUID4、UUID5和UUID6,UUID6的末尾句柄为0x04FF。

客户端接着发起第三次请求,起始句柄设为0x0500。

服务端返回错误,错误原因是未找到属性。客户端根据该错误原因,判断已经获取服务端设备的全部主要服务项。

按UUID发现主要服务项

该功能向服务端设备发送Read By Group Type Request,起始句柄为0x0001,结束句柄为0xFFFF,属性类型为0x2800(主要属性的UUID),指定的UUID为xxxx,查找全部符合条件的主要服务项。

具体的操作步骤与“发现全部主要服务”一致。

通常具有指定UUID的服务项仅有一个。

3.3 发现关系

该功能呢包含一个子功能:查找包含的服务项。

该功能向服务端设备发送Read By Type Request,起始句柄为0x0001,结束句柄为0xFFFF,属性类型为0x2802(包含的声明UUID),查找全部符合条件的被包含服务项。

服务端返回响应,包含了满足条件的服务项的句柄和属性值。

3.4 发现特征项

该功能包含两个子功能:发现服务项下的全部特征项,按UUID发现特征项。

发现服务项下的全部特征项

该功能向服务端设备发送Read By Type Request,设置已知的服务项首末句柄,属性类型为0x2803(特征项声明的UUID),查找全部符合条件的特征项。

服务端返回Read By Type Response,响应中包含多个“属性句柄 – 值”元素组成的列表。单个元素包含三个参数:元素长度、特征项声明的句柄和特征值参数。特征值参数包括特征值的功能特性、特征值句柄和UUID。

因为该列表长度不能超过一个属性层的PDU长度,所以需要多次执行请求和响应,直到服务端设备返回“未找到属性项”或到达结束句柄,才能获取全部的特征项,如下图所示:

GATT_Discovery_Characteristic

观察上图,客户端第一次发起请求,查找特征项,首末句柄分别是0x0200和0x0214。

服务端返回响应中包含两个元素,每个元素代表一个特征项。每个元素的长度为0x07。第一个特征项的声明句柄为0x0203,特征值的功能特性为0x02,即具有Read功能,特征值的句柄为0x0204,特征值的UUID为UUID1。第二个特征项的声明句柄为0x0210,特征值的功能特性为0x02,即具有Read功能,特征值的句柄为0x0212,特征值的UUID为UUID2。

由于每个元素的长度为7,表明该两个特征值的UUID均是2字节UUID,如果是16字节UUID,则每个元素的长度应该为0x15。

按UUID发现特征项

该功能根据已知的特征项UUID和首末句柄范围,查找满足条件的 特征项。

具体与“发现服务项下的全部特征项”完全一致。

3.5 发现描述符

该功能包含一个子功能:发现全部描述符。

该功能向服务端设备发送Find Information Request,设置已知的特征项首末句柄,查找全部符合条件的描述符。

服务端返回Find Information Response,响应中包含多个“属性句柄 – 值”元素组成的列表。单个元素包含三个参数:UUID格式、特征值的句柄和描述符的UUID。如果UUID格式参数等于1,表示描述符的UUID为2字节UUID,如果等于2,表示描述符的UUID为16字节UUID。

因为该列表长度不能超过一个属性层的PDU长度,所以需要多次执行请求和响应,直到服务端设备返回“未找到属性项”或到达结束句柄,才能获取全部的描述符,如下图所示:

GATT_Discovery_Descriptor

观察上图,服务端的响应数据第一个参数0x01表示UUID1和UUID2均为2字节UUID,第二个参数0x0205表示该描述符上级的特征值的句柄。

3.6 读特征值

该功能包含四个子功能:读特征值,按UUID读特征值,读长包特征值,读多个特征值。

读特征值

客户端已知特征值句柄,向服务端发送Read Request读取该句柄的特征值。

服务端返回指定句柄的特征值。该特征值长度应小于等于(ATT_MTU-1),如果大于该限制,则仅返回前(ATT_MTU-1)个数据。

下图为一次读取过程:

GATT_Read_Request

按UUID读特征值

客户端已知特征值的UUID,不知道其句柄,向服务端发送Read By Type Request读取该特征值。

具体操作与“按UUID发现特征项”一致。

读长包特征值

客户端已知特征值的句柄,但是特征值的长度大于(ATT_MTU-1),向服务端发送Read Request以读取前(ATT_MTU-1)个字节,然后发送Read Blob Request并设置合适的偏移量,以读取随后的(ATT_MTU-1)个字节,重复执行Read Blob Request直到服务端的Read Blob Response内容小于(ATT_MTU-1),表明该特征值完全被读取。

具体步骤如下:

GATT_Read_Blob_Request

读多个特征值

客户端已知多个特征值的句柄,向服务端发送Read Multiple Request,参数为多个特征值句柄。

服务端返回Read Multiple Response,包含了多个指定的特征值数据。

3.7 写特征值

该功能包含五个子功能:写命令,带签名的写命令,写请求,写长包请求,可靠的写请求。

写命令

客户端已知特征值句柄,向服务端发送Write Command,写入指定数据。

数据长度不能超过(ATT_MTU-3)字节,如果超过,仅写入前(ATT_MTU-3)个字节。

该命令无需服务端返回响应。

带签名的写命令

客户端已知特征值句柄,且链接没有经过认证,向服务端发送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)。

两种请求均需要对应的服务端响应。

一个写长包请求流程如下:

GATT_Long_Write_Request

可靠的写请求

客户端已知特征值句柄,希望一次性写入多字节的数据,或者要求数据的每个字节都必须被安全写入服务端设备,向服务端发送Prepare Write Request,偏移量永远等于0,一次性只发送一个数据,带多字节数据缓存完毕,再发送Execute Write Request执行写请求。

具体的操作与“写长包请求”完全一致。

3.8 通知

该功能包含一个子功能:通知。

服务端执行Handle Value Notification,参数为特征值句柄和通知数据,向客户端推送通知。

执行通知前,该特征值需要已经使能通知,并且将通知数据写入该特征值。

该命令无需客户端返回响应。

3.9 指示

该功能包含一个子功能:指示。

服务端执行Handle Value Indication,参数为特征值句柄和指示数据,向客户端推送指示。

执行指示前,该特征值需要已经使能指示,并且将指示数据写入该特征值。

该命令需要客户端返回响应Handle Value Confirmation。

3.10 读写描述符

该功能包含四个子功能:读描述符,读长包描述符,写描述符,写长包描述符。

读描述符与读特征值一致。

读长包描述符与读长包特征值一致。

写描述符与写请求一致。

写长包描述符与写长包请求一致。

4. 与L2CAP层互操作

GATT使用的L2CAP固定信道传输属性数据。

GATT的PDU长度限制ATT_MTU默认大小为23,它与L2CAP层的MTU值保持一致。

(完)