特性
快速配对服务
快速配对提供方应具有以下 GATT 服务。
| 服务 | UUID |
|---|---|
| 快速配对服务 | 0xFE2C |
此服务应具有以下特征。
| 快速配对服务特征 | 已加密 | 权限 | UUID |
|---|---|---|---|
| 模型 ID | 否 | 读取 | FE2C1233-8366-4814-8EB0-01DE32100BEA |
| 基于密钥的配对 | 否 | 写入和通知 | FE2C1234-8366-4814-8EB0-01DE32100BEA |
| 通行密钥 | 否 | 写入和通知 | FE2C1235-8366-4814-8EB0-01DE32100BEA |
| 账号密钥 | 否 | 写入 | FE2C1236-8366-4814-8EB0-01DE32100BEA |
设备信息服务
快速配对提供方还应支持设备信息服务。
| 服务 | UUID |
|---|---|
| 设备信息服务 | 0x180A |
快速配对搜索器使用以下特征。
| 名称 | 已加密 | 权限 | UUID |
|---|---|---|---|
| 固件修订版本 | 否 | 读取 | 0x2A26 |
特征:型号 ID
此特征允许 Seeker 在设备以可发现模式进行广播之外的时间根据需要读取模型 ID。它应始终返回以下数据:
| 八位字节 | 数据类型 | 说明 | 值 |
|---|---|---|---|
| 0 - 2 | uint24 |
模型 ID | 不定 |
特征:基于密钥的配对
此特征控制基于密钥的配对程序。在此过程中,通过验证寻求者和提供者是否都拥有预共享密钥来建立一定程度的信任。每种情况下的密钥都不同:
情形 1:预共享密钥基于防欺骗公钥/私钥对和 Seeker 自身的公钥/私钥对,后者在每次配对尝试时都会发生变化。
- 提供方处于配对模式。
- 寻求者验证提供者是否拥有防欺骗私钥。
请注意,在配对模式下,提供程序当然也可以通过常规方式进行配对,例如与不支持 Fast Pair 的基于密钥的配对的设备进行配对。
情形 2:预共享密钥是账号密钥之一。
- 提供方通常不处于配对模式。(但这不是必需的 - 提供方应支持即使在配对模式下也使用账号密钥。)
- 寻求者和提供者各自验证对方是否拥有账号密钥。
由于这两种情况非常相似,只是使用的预共享密钥不同,因此在流程中将它们合并在一起。
数据格式
如需了解每种格式的使用方式,请参阅程序。
| 八位字节 | 数据类型 | 说明 | 值 | 是否强制? |
|---|---|---|---|---|
| 0 - 15 | uint128 |
加密请求 | 不定 | 强制 |
| 16 - 79 | 公钥 | 不定 | 可选 |
表 1.1:由搜索者写入特征的加密请求。
| 八位字节 | 数据类型 | 说明 | 值 | 是否强制? |
|---|---|---|---|---|
| 0 | uint8 |
消息类型 | 0x00 = 基于密钥的配对请求 |
强制 |
| 1 | uint8 |
标志
|
各不相同 | 强制 |
| 2 - 7 | uint48 |
满足以下任一条件:
|
各不相同 | 强制 |
| 8 - 13 | uint48 |
搜索者的 BR/EDR 地址 | 各不相同 | 仅当标志位 1 或 3 设置时才存在 |
| n - 15 | 随机值(盐值) | 各不相同 | 强制 |
表 1.2.1:原始请求(类型 0x00)。从 表 1.1 中的加密请求解密而来。
| 八位字节 | 数据类型 | 说明 | 值 | 是否强制? |
|---|---|---|---|---|
| 0 | uint8 |
消息类型 | 0x10 = 操作请求 |
强制 |
| 1 | uint8 |
标志 | 各不相同 | 强制 |
| 2 - 7 | uint48 |
满足以下任一条件:
|
各不相同 | 强制 |
| 8 | uint8 |
发起群聊 | 各不相同 | 如果设置了标志位 0,则此字段为必填字段 |
| 9 | uint8 |
消息代码 | 各不相同 | 如果设置了标志位 0,则此字段为必填字段 |
| 10 | uint8 |
取决于标志:
|
各不相同 | 如果设置了标志位 0 或 1,则此项为必需项 |
| 11 - n | 附加数据 | 各不相同 | 可选 | |
| n - 15 | 随机值(盐值) | 各不相同 | 强制 |
表 1.2.2:原始请求(类型 0x10)。从 表 1.1 中的加密请求解密而来。
| 八位字节 | 数据类型 | 说明 | 值 |
|---|---|---|---|
| 0 | uint8 |
消息类型 | 0x01 = 基于密钥的配对响应 |
| 1 - 6 | uint48 |
提供方的公开 (BR/EDR) 地址 | 各不相同 |
| 7 - 15 | 随机值(盐值) | 各不相同 |
表 1.3:原始响应。加密,以生成 表 1.4 中的加密响应。
| 八位字节 | 数据类型 | 说明 | 值 |
|---|---|---|---|
| 0 -15 | uint128 |
加密的响应 | 各不相同 |
表 1.4:提供方通过通知发送给寻求方的加密响应。
特征:通行密钥
| 八位字节 | 数据类型 | 说明 | 值 |
|---|---|---|---|
| 0 - 15 | uint128 |
加密的通行密钥块 | 各不相同 |
表 2.1:加密的通行密钥块。如需了解使用方法,请参阅基于密钥的配对程序。
| 八位字节 | 数据类型 | 说明 | 值 |
|---|---|---|---|
| 0 | uint8 |
消息类型 | 以下值之一:
|
| 1 - 3 | unit32 |
6 位数通行密钥 | 各不相同 |
| 4 - 15 | 随机值(盐值) | 各不相同 |
表 2.2:原始通行密钥块。 表 2.1 的解密版本。
特征:账号密钥
配对后,快速配对搜索器会将账号密钥写入快速配对提供方。
| 八位字节 | 数据类型 | 说明 | 值 |
|---|---|---|---|
| 0 - 15 | uint128 |
账号密钥(已加密) | 各不相同 |
在收到写入请求后,快速配对提供程序应执行以下操作:
- 使用在过程的第 4 步中生成的共享密钥令牌解密账号密钥。
- 对于需要配对的提供程序(常见):
- 在解密之前,请验证共享密钥令牌是否用于解密第 12 步中的通行密钥请求。如果此步骤未使用此密钥通过,请忽略此写入并退出。
- 此时,共享密钥令牌(过程中的 K)将不会再次用于此配对。任何使用此密钥加密且未重新启动过程的请求都应被拒绝。
- 对于需要配对的提供程序(常见):
- 验证解密后的值是否以
0x04或0xFF开头。如果不是,则忽略此写入并退出。- 如果值为
0x04:- 检查持久性 Account Key 列表是否有空间容纳新值。
- 如果不是,则从列表中删除最近最少使用的值。
- 将新值添加到列表中。
- 如果值为
0xFF:
- 如果值为
列表中的账号密钥在基于密钥的配对期间使用。
特征:固件修订版本
此特征允许 Seeker 根据需要读取 Provider 的固件修订版本。它应始终返回以下数据:
| 八位字节 | 数据类型 | 说明 | 值 |
|---|---|---|---|
| 0 - var | utf8s |
固件修订版本代码 | 各不相同 |
即使提供程序上有多个固件(例如,左侧耳机、右侧耳机和耳机盒各有一个固件),也应将其封装到单个 UTF-8 字符串中。提供程序还可以针对特殊情况返回特定字符串:
status-updating:如果提供方当前正在更新到新固件,则为该值。 或者,提供方可以返回已暂存固件的版本。
status-abnormal:如果提供方处于异常状态。例如,由于固件更新失败而出现故障。此值将导致 Seeker 显示一条消息,告知用户必须立即更新。
提供方应限制对固件修订版本特征的访问,以防止设备跟踪。建议的限制:
- 已配对的设备应随时可以访问
- 当提供程序可被发现时,任何设备都应具有访问权限
特征:其他数据
此服务应具有以下特征。
| 快速配对服务特征 | 已加密 | 权限 | UUID |
|---|---|---|---|
| 数据 | 否 | 写入和通知 | FE2C1237-8366-4814-8EB0-01DE32100BEA |
| 旧的快速配对服务特征(目标是在 2021 年 1 月 1 日弃用) | 已加密 | 权限 | UUID |
|---|---|---|---|
| 数据 | 否 | 写入和通知 | 0x1237 |
在写入或通知此特征之前,必须通过特征 FE2C1234-8366-4814-8EB0-01DE32100BEA 进行握手以获得共享密钥令牌。AES-CTR 将用于加密流经此特征的数据,其算法定义如下。此模式在处理超出单个 16 字节块的数据时更安全。HMAC-SHA256 将用于确保数据完整性,具体定义如下。
| 八位字节 | 说明 | 值 |
|---|---|---|
| 0 - 7 | HMAC-SHA256 的前 8 个字节。 | 各不相同 |
| 8 - 15 | 由 AES-CTR 加密使用的随机数。 | 各不相同 |
| 16 - var | 加密数据。 | 各不相同 |
表 3.1:由提供方通过通知发送给寻求方或由寻求方通过写入发送给提供方的数据包。
| 八位字节 | 数据类型 | 说明 | 值 |
|---|---|---|---|
| 0 - var | byte array |
数据 | 各不相同,请根据表 1.2.2 的数据 ID 对其进行解码:
|
表 3.2:原始数据。从 表 3.1 中的加密数据解密而来。
当请求通知时(例如,通过表 1.2.1 中的 Bit 2 请求个性化名称),快速配对提供方应执行以下操作:
- 为 Nonce 生成加密随机的 8 个字节。
使用 AES-CTR 加密数据,其中每个 16 字节块都是使用以下方式生成的
encryptedBlock[i] = clearBlock[i] ^ AES(key, concat((uint8) i, 0x00000000000000, nonce))其中
- AES 密钥是流程中第 4 步的共享密钥。
- clearBlock[i] 是从 data[i * 16] 开始的 16 字节块。最后一个块可以小于 16 字节。
执行 concat(encryptedBlock[0], encryptedBlock[1],…) 以创建加密数据。
通过以下方式生成 HMAC-SHA256
sha256(concat((K ^ opad), sha256(concat((K ^ ipad), concat(nonce, encrypted_data)))))其中
- K 由 concat(shared_secret, 48-byte ZEROs) 生成,shared_secret 来自流程中的第 4 步。
- opad 是 64 字节的外部填充,由值为
0x5C的重复字节组成。 - ipad 是 64 字节的内部填充,由值为
0x36的重复字节组成。
从 HMAC-SHA256 中提取前 8 个字节作为数据包的前缀。
在收到写入请求后,快速配对提供程序应执行以下操作:
- 通过检查 HMAC-SHA256 的前 8 个字节来验证数据的完整性。
使用 AES-CTR 解密加密的数据,其中每个块都是使用以下方式生成的
clearBlock[i] = encryptedBlock[i] ^ AES(key, concat((uint8) i, 0x00000000000000, nonce))其中
执行 concat(clearBlock[0], clearBlock[1],…) 以创建原始数据。