前言
最近自己搞了些智能家居的小节点,但由于wifi入网方式功耗太高,于是关注起了蓝牙
bthome是一种灵活的低功耗BLE数据格式协议,用于广播传感器数据,此协议支持数据加密,目前最新为v2版本。在home assistant中也支持协议,这意味着自己DIY的传感器采集节点,通过BLE广播数据时只要遵守此协议,该节点就能被HA探测到并解析传感器数据。
协议官方说明见https://bthome.io/format/,但我在阅读过程中由于我个人阅读理解能力有限,感觉其文档术语混乱,结构混乱,所以根据自己理解写下这篇文章
协议分析
此协议只是对蓝牙数据包中PDU中数据格式的规定,蓝牙Link Layer层数据包格式如下
bthome对payload中的数据格式作出了规定。
bthome协议分析
bthome协议支持对传感器数据加密,根据是否加密与否,其数据结构也稍有不同,本文将其简称为非加密结构和加密结构
1. 非加密结构
-
head
头部长度3字节,对于bthome来说是固定的:0x02 0x01 0x06
-
local name
长度为type+name长度,type分为Shortened local name (0x08)、Complete local name (0x09)
name部分为字符的ASCII码
-
len
service data长度计算为service flag+UUID+device info+BThome data,也就是len位之后所有数据的长度
-
service flag
此部分名称官方没有明确的表述,我暂且称其为service flag,对于此协议固定为0x16
-
UUID
对于此协议固定为0xfcd2,但由于UUID需要按字节反向读取,所以在数据包中需要写入0xd2 0xfc
-
device info
长度为1字节,其各位含义如下,对于非加密数据包为0x40
- bit 0: “Encryption flag”,0表示传感器数据没有加密,反之加密
- bit 1-4: “Reserved for future use”
- bit 5-7: “BTHome Version”,目前协议版本是V2所以其二进制为010
-
sensor data
此部分是存放传感器数据,也是启用加密时的加密对象,此部分定义参考https://bthome.io/format/
2. 加密结构
加密结构前面部分与非加密结构相同,因此本小节只描述加密相关结构
bthome协议对数据的加密采用AES-CCM,AES-CCM在加密数据的同时输出数据摘要(tag、digest),摘要可用于校验数据完整性。被加密部分为sensor data,加密完成后需要在数据包中写入加密时的counter、tag以供对端解密数据。
下面以mbedtls中的加密api来进行讲解
在进行加密前需要调用mbedtls_ccm_setkey设置加密密匙
int mbedtls_ccm_setkey ( mbedtls_ccm_context * ctx,
mbedtls_cipher_id_t cipher,
const unsigned char * key, //密匙数据指针
unsigned int keybits //密匙数据长度(bit长度)
)
再调用mbedtls中的mbedtls_ccm_encrypt_and_tag函数来进行加密,其定义和解释如下
int mbedtls_ccm_encrypt_and_tag ( mbedtls_ccm_context * ctx,
size_t length, //被加密数据长度
const unsigned char * iv, //nonce数据指针
size_t iv_len, //nonce数据长度
const unsigned char * add, //额外数据
size_t add_len, //额外数据长度
const unsigned char * input,//被加密数据指针
unsigned char * output, //存放输出密文的数据指针
unsigned char * tag, //存放输出tag(digest)的数据指针
size_t tag_len //指定输出tag的长度,必须为4, 6, 8, 10, 14 或 16
)
对于nonce的结构为 [mac addr] + [UUID] + [device info] + [counter] 总长度为13字节
- mac addr长度为6字节
- UUID长度为2字节
- device info长度1字节
- counter长度4字节,初始值可以自己设置,在每次发送包后对其加一
对于bthome协议,tag长度需要为4字节。
所以在加密中输入为nonce、密匙、counter、明文,输出为密文、tag(digest)。将密文、counter、tag加密到数据包中即可组成完整的数据包。
下面是对一个bthome数据包的解析,以供参考
02 01 06 [HEAD]
04 09 65 6e 66 [Complete name size: 0x4]
12 [service data len 0x12]
| 16 [service flag]
| d2 fc [UUID ]
| 41 [device info]
| 2f 0a fa 54 37 e3 [sensor data]|
| 00 11 22 33 [counter]
| b8 ed 87 a7 [tag]
下面是我在开源项目基础上完善了bthome协议在esp32上的实现,并使用此协议采集传感器数据的demo
https://github.com/junchao98/bthome-weather-station