ProtocolParser/README.md

270 lines
19 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# ProtocolParser
协议解析器
# 通讯协议解析
- [通讯协议解析](#通讯协议解析)
- [1. 概述](#1-概述)
- [2. 密钥](#2-密钥)
- [3. 认证流程](#3-认证流程)
- [3.1 非对称双向认证](#31-非对称双向认证)
- [3.2 对称双向认证](#32-对称双向认证)
- [4. 数据结构](#4-数据结构)
- [4.1 基础帧base\_frame](#41-基础帧base_frame)
- [4.2 数据帧data\_frame](#42-数据帧data_frame)
- [4.3 数字信封digi\_env](#43-数字信封digi_env)
- [4.4 非对称双向认证包ssl\_frame](#44-非对称双向认证包ssl_frame)
- [4.5 对称双向认证包hmac\_frame](#45-对称双向认证包hmac_frame)
- [4.6 对称加密数据帧crypto\_zdata\_frame](#46-对称加密数据帧crypto_zdata_frame)
- [4.7 设备信息存储device](#47-设备信息存储device)
- [5. 接口解析](#5-接口解析)
- [5.1 协议封装/解析](#51-协议封装解析)
- [5.1.1 void protocal\_wrapper(data\_frame \*frame, u8 type, u16 length, u8 \*data, bool use\_crc)](#511-void-protocal_wrapperdata_frame-frame-u8-type-u16-length-u8-data-bool-use_crc)
- [5.1.2 void base\_frame\_maker(void \*in\_frame, base\_frame \*out\_frame, u16 dest\_addr,device \*dev,u16 node\_addr=0)](#512-void-base_frame_makervoid-in_frame-base_frame-out_frame-u16-dest_addrdevice-devu16-node_addr0)
- [5.1.3 bool base\_frame\_parser(base\_frame \*in\_frame, void \*\*out\_frame, device \*dev)](#513-bool-base_frame_parserbase_frame-in_frame-void-out_frame-device-dev)
- [5.1.4 void ssl\_frame\_maker(ssl\_frame \*frame, u8 \*data, int data\_len)](#514-void-ssl_frame_makerssl_frame-frame-u8-data-int-data_len)
- [5.1.5 void zigbee\_data\_encrypt(data\_frame \*data, crypto\_zdata\_frame *zdata, bool (* SM4\_encrypt)(u8 \*key\_origin, u32 key\_len, u8 \*in\_origin, u32 in\_len, u8 \*out, u32 \*out\_len),QString en\_key = "")](#515-void-zigbee_data_encryptdata_frame-data-crypto_zdata_frame-zdata-bool--sm4_encryptu8-key_origin-u32-key_len-u8-in_origin-u32-in_len-u8-out-u32-out_lenqstring-en_key--)
- [5.1.6 bool zigbee\_data\_dectypt(data\_frame \*data, crypto\_zdata\_frame *zdata,bool (* SM4\_decrypt)(u8 \*key\_origin, u32 key\_len, u8 \*in, u32 in\_len, u8 \*out, u32 \*out\_len),QString en\_key = "")](#516-bool-zigbee_data_dectyptdata_frame-data-crypto_zdata_frame-zdatabool--sm4_decryptu8-key_origin-u32-key_len-u8-in-u32-in_len-u8-out-u32-out_lenqstring-en_key--)
- [5.2 协议认证/验证](#52-协议认证验证)
- [5.2.1 void HMAC\_identify(device \*self, device \*node, hmac\_frame \*hframe, void (\*sendTonode)(ZigbeeFrame \&data), void (\*SM3\_HMAC)(u8 \*key, int keylen,u8 \*input, int ilen,u8 output\[32\] ))](#521-void-hmac_identifydevice-self-device-node-hmac_frame-hframe-void-sendtonodezigbeeframe-data-void-sm3_hmacu8-key-int-keylenu8-input-int-ilenu8-output32-)
- [5.2.2 bool data\_frame\_verify(data\_frame \*frame)](#522-bool-data_frame_verifydata_frame-frame)
- [5.2.3 void HMAC\_changeVerifykey(u8 key\[16\], device\* self, device \*node, void (*sendTonode)(ZigbeeFrame \&data),bool (* SM4\_encrypt)(u8 \*key\_origin, u32 key\_len, u8 \*in\_origin, u32 in\_len, u8 \*out, u32 \*out\_len))](#523-void-hmac_changeverifykeyu8-key16-device-self-device-node-void-sendtonodezigbeeframe-databool--sm4_encryptu8-key_origin-u32-key_len-u8-in_origin-u32-in_len-u8-out-u32-out_len)
- [5.3 工具](#53-工具)
- [5.3.1 uint16\_t crc16\_xmodem(const uint8\_t \*buffer, uint32\_t buffer\_length)](#531-uint16_t-crc16_xmodemconst-uint8_t-buffer-uint32_t-buffer_length)
- [5.3.2 bool bytecmp(u8 \*a, u8 \*b, u16 length)](#532-bool-bytecmpu8-a-u8-b-u16-length)
## 1. 概述
本协议是一套运行在应用层的,用于端到端通讯的轻量级安全协议,支持非对称/对称的双向、单向认证及加密通讯。该协议的认证和加密通讯由SM系列SM2、SM3、SM4算法保障安全性较强可抵御重放攻击。协议易拓展数据包采用二进制传输适合在多种窄带宽场景下使用目前已在嵌入式、ARM、X86/64平台上测试通过
## 2. 密钥
本协议采用两类密钥一是用于双向认证的SM2公私钥对二是用于对称双、单向认证和加密传输的SM4密钥。
+ SM2密钥长度为公钥64Bytes512Bits私钥32 Bytes256Bits。密文长度为明文长度再加上96Bytes。签名长度为64Bytes被签名数据内包含随机数盐值因此对同一数据签名的结果不相同
+ SM4密钥长度为16Bytes128Bits
## 3. 认证流程
### 3.1 非对称双向认证
由于SM2算法会占用一部分资源非对称双向认证一般用于性能较好的终端如性能较强的单片机、树莓派或者PC等设备之间的认证性能较差的单片机将会出现死机等预料外情况板载硬件密码算法模块除外
```mermaid
sequenceDiagram
participant 服务器
participant 客户端
客户端-->服务器: 客户端验证服务器
客户端->>服务器: 包含Hello信息的ssl_frame请求
服务器->>客户端: 包含Hello和64Bytes服务器公钥信息的ssl_frame响应
客户端-->>客户端: 用服务器公钥加密自己的公钥
客户端->>服务器: 包含服务器公钥加密的客户端公钥信息的ssl_frame数据包
服务器-->>服务器: 用自己的私钥解密出客户端公钥
服务器->>客户端: 包含用客户端公钥加密的Verified信息的ssl_frame数据包
客户端-->>客户端: 用自己的私钥解密出Verified信息
客户端->>服务器: 包含服务器公钥加密的OK信息的ssl_frame数据包
服务器-->客户端: 服务器验证客户端
服务器-->>服务器: 生成8Bytes随机数挑战值
服务器->>客户端: 包含客户端公钥加密的8Bytes挑战值信息的ssl_frame数据包
客户端-->>客户端: 用自己的私钥解密出挑战值,<br>并用自己的私钥对它签名
客户端->>服务器: 包含客户端公钥64Bytes、签名64Bytes、<br>挑战值8Bytes共136Bytes数据的ssl_frame数据包
服务器-->>服务器: 进行三轮比对:<br>1.将获得的客户端公钥与<br>第一轮验证中获取的客户端公钥进行比对<br>2.将获得的挑战值与发送的进行比对<br>3.用客户端公钥对挑战值签名数据进行验签
服务器->>客户端: 包含客户端公钥加密的OK信息的ssl_frame数据包
服务器-->客户端: 双向认证结束
```
### 3.2 对称双向认证
对称双向认证使用基于SM3的HMAC算法实现。通过验证双方是否共同持有同一对预设定密钥来实现认证在实际运用过程中为节省资源服务器发送给客户端的Identified信息可不加密采用明文传输因为后续传输的数据包均为加密数据包
```mermaid
sequenceDiagram
participant 服务器
participant 客户端
客户端-->>客户端: 生成1Byte随机数并对其使用基于SM3的HMAC算法和<br>预设定16Bytes密钥生成值
客户端->>服务器: 包含随机数和使用预设定16Bytes密钥生成的<br>HMAC值信息的hmac_frame请求
服务器-->>服务器: 使用自己拥有的预设定16Bytes密钥对收到的<br>1Byte随机数进行HMAC运算并将值与收到值进行比对
服务器->>客户端: 包含使用预设定16Bytes密钥加密的<br>Identified信息的crypto_zdata_frame数据包
客户端-->>客户端: 用预设定16Bytes密钥解密出Identified信息
客户端->>服务器: 包含正常数据的用预设定密钥加密的<br>crypto_zdata_frame数据包
```
## 4. 数据结构
### 4.1 基础帧base_frame
基础帧是本协议传输最底层的帧,用于承载其他帧。
| 帧结构 | 长度Bytes | 说明 |
|:----:|:----:|:----:|
| head | 2 | 包头,数值固定为`0xAAAD` |
| ori_addr | 2 | 源地址,即该帧发送方的地址 |
| des_addr | 2 | 目的地址,即该帧接收方的地址 |
| node_addr | 2 | 节点地址,仅在此帧被服务器进行路由时使用,<br>用以标记该帧由哪个节点最先产生 |
| id | 2 | 帧标号,用以与`rand_num`共同唯一标记一个帧,防止重放攻击 |
| length | 2 | 帧长度包括帧头最大为65535个Bits |
| reset_num | 1 | 重置标记位,用以告知对方在接收此帧后,<br>下一帧的帧标号将归零,`rand_num`将被重新生成 |
| rand_num | 1 | 随机数 |
| data | 可变长度 | 数据最大不超过8191 - `BASE_FRAME_PREFIX_LEN`= 8177 |
另有其他说明如下:
+ 宏定义`BASE_FRAME_PREFIX_LEN`基础帧帧头长度固定为14Bytes
+ 宏定义`BASE_FRAME_HEAD`:基础帧帧头,固定为`0xAAAD`
+ 宏定义`BASE_FRAME_RESET_NUM`:基础帧重置阈值:当发送帧数达到该阈值时,下一帧将被重置帧标号和随机数,该阈值一般为`10000`,最大不超过`65535`
+ 宏定义`new_base_frame(num)`:用以生成基础帧数据结构,`num`为`data`部分长度,使用时请注意强制类型转换
### 4.2 数据帧data_frame
数据帧是本协议传输中最顶层的帧,用于承载各类数据。
| 帧结构 | 长度Bytes | 说明 |
|:----:|:----:|:----:|
| head | 2 | 包头,数值固定为`0xAAAA` |
| type | 1 | 数据类型,由用户根据业务不同自定义 |
| use_crc | 1 | CRC标记位当其为`0xFF`时将对数据进行CRC校验 |
| data_length | 2 | 数据长度,不包括包头 |
| crc | 2 | CRC16校验码当CRC标记位使能且对数据进行CRC校验后数值与该码不同包将被判定为损坏 |
| data | 可变长度 | 根据底层帧的不同数据最大不超过8191 - `BASE_FRAME_PREFIX_LEN` - `DATA_FRAME_PREFIX_LEN`= 8169若叠加多重帧需要减去对应帧的帧头 |
另有其他说明如下:
+ 宏定义`DATA_FRAME_PREFIX_LEN`数据帧帧头长度固定为8Bytes
+ 宏定义`DATA_FRAME_HEAD`:数据帧帧头,固定为`0xAAAA`
+ 宏定义`new_data_frame(num)`:用以生成数据帧数据结构,`num`为`data`部分长度,使用时请注意强制类型转换
### 4.3 数字信封digi_env
数字信封是通信双方进行非对称双向认证后,用于加密传输数据的数据结构。
| 帧结构 | 长度Bytes | 说明 |
|:----:|:----:|:----:|
| head | 2 | 包头,数值固定为`0xAAAB` |
| length | 2 | 长度,包括包头 |
| crypted_session_key | 112 | 由接收方公钥加密的16Bytes会话密钥再加上96Bytes的额外数据 |
| data | 可变长度 | 由会话密钥进行SM4加密的数据长度数据最大不超过8191 - `BASE_FRAME_PREFIX_LEN` - `DIGI_ENV_PREFIX_LEN` - `DIGI_ENV_SESSION_KEY_LEN`= 8061又因SM4的密文为16的倍数因此最大不能超过8048 |
另有其他说明如下:
+ 宏定义`SM4_PADDING_LEN`SM4默认填充长度固定为16Bytes
+ 宏定义`DIGI_ENV_PREFIX_LEN`数字信封包头长度固定为4Bytes
+ 宏定义`DIGI_ENV_SESSION_KEY_LEN`数字信封密态会话密钥长度固定为112Bytes
+ 宏定义`DIGI_ENV_HEAD`:数字信封包头,固定为`0xAAAB`
+ 宏定义`new_digi_env(num)`:用以生成数字信封数据结构,`num`为`data`部分长度,使用时请注意强制类型转换
### 4.4 非对称双向认证包ssl_frame
非对称双向认证包负责承载非对称双向认证相关数据。
| 帧结构 | 长度Bytes | 说明 |
|:----:|:----:|:----:|
| head | 2 | 包头,数值固定为`0xAAAC` |
| length | 2 | 长度,包括包头 |
| data | 可变长度 | 数据最大不超过8191 - `BASE_FRAME_PREFIX_LEN` - `SSL_FRAME_PREFIX_LEN`= 8173 |
另有其他说明如下:
+ 宏定义`SSL_FRAME_PREFIX_LEN`非对称双向认证包包头长度固定为4Bytes
+ 宏定义`SSL_FRAME_HEAD`:非对称双向认证包包头,固定为`0xAAAC`
+ 宏定义`new_ssl_frame(num)`:用以生成非对称双向认证包数据结构,`num`为`data`部分长度,使用时请注意强制类型转换
### 4.5 对称双向认证包hmac_frame
对称双向认证包负责承载对称双向认证相关数据。
| 帧结构 | 长度Bytes | 说明 |
|:----:|:----:|:----:|
| head | 2 | 包头,数值固定为`0xAAAE` |
| length | 2 | 长度,包括包头 |
| value | 1 | 随机数 |
| hmac | 32 | 随机数`value`对应的HMAC数值 |
另有其他说明如下:
+ 宏定义`HMAC_FRAME_PREFIX_LEN`对称双向认证包包头长度固定为4Bytes
+ 宏定义`HMAC_FRAME_HEAD`:非对称双向认证包包头,固定为`0xAAAE`
### 4.6 对称加密数据帧crypto_zdata_frame
对称加密数据帧用于通信双方完成对称双向认证后数据加密传输。
| 帧结构 | 长度Bytes | 说明 |
|:----:|:----:|:----:|
| head | 2 | 包头,数值固定为`0xAAAF` |
| length | 2 | 长度,包括包头 |
| data | 可变长度 | 数据最大不超过8191 - `BASE_FRAME_PREFIX_LEN` - `CRYPTO_ZDATA_FRAME_PREFIX_LEN`= 8173 |
另有其他说明如下:
+ 宏定义`CRYPTO_ZDATA_FRAME_PREFIX_LEN`对称加密数据帧帧头长度固定为4Bytes
+ 宏定义`CRYPTO_ZDATA_FRAME_HEAD`:对称加密数据帧帧头,固定为`0xAAAF`
### 4.7 设备信息存储device
协议采用`device`结构体用作存储相关通讯数据。
| 结构 | 说明 |
|:----:|:----:|
| addr | 用以保存通讯对象的地址(可以是发送方,也可以是接收方) |
| id | 缓存发送/接收到的基础帧帧标号 |
| rand_num | 缓存发送/接收到的基础帧随机数 |
| verified | 标记该通讯对象是否通过认证 |
| online | 标记该通讯对象是否在线 |
| logined | 仅作为服务器时使用,记录该通讯对象是否已经登录 |
| stage | 仅在非对称双向认证时使用,记录该通讯对象正处于认证的第几阶段 |
| chlg_buf | 仅在非对称双向认证时使用,记录该通讯对象的挑战值 |
| ip | 仅在TCP链路中使用记录该通讯对象的IP地址 |
| port | 仅在TCP链路中使用记录该通讯对象的端口号 |
| key_pair | 仅在非对称双向认证时使用,记录该通讯对象的公钥和会话密钥 |
## 5. 接口解析
### 5.1 协议封装/解析
#### 5.1.1 void protocal_wrapper(data_frame *frame, u8 type, u16 length, u8 *data, bool use_crc)
将对应数据打包进数据帧中。
| 变量名 | 输入/输出 | 说明 |
|:----:|:----:|:----:|
| frame | 输出 | 数据帧指针 |
| type | 输入 | 数据类型 |
| length | 输入 | 数据长度 |
| use_crc | 输入 | 是否启用CRC验证 |
#### 5.1.2 void base_frame_maker(void *in_frame, base_frame *out_frame, u16 dest_addr,device *dev,u16 node_addr=0)
将对应数据打包进基础帧中。
| 变量名 | 输入/输出 | 说明 |
|:----:|:----:|:----:|
| in_frame | 输入 | 数据包指针,指向需要被打包进基础帧的数据地址 |
| out_frame | 输出 | 基础帧指针 |
| dest_addr | 输入 | 目的地址 |
| dev | 输出 | 发送方的`device`数据结构指针,缓存帧标号和随机数等相关数据 |
| node_addr | 输入 | 仅在服务器端路由时使用,用以保存该基础帧的最初创建者地址 |
#### 5.1.3 bool base_frame_parser(base_frame *in_frame, void **out_frame, device *dev)
从基础帧中解析出数据,返回`true`为解析成功,`false`为解析失败,需要检查包基础帧数据内容是否错误。
| 变量名 | 输入/输出 | 说明 |
|:----:|:----:|:----:|
| in_frame | 输入 | 基础帧指针 |
| out_frame | 输出 | 数据包指针,指向需要从基础帧中解析出的数据包地址 |
| dev | 输出 | 接收方的`device`数据结构指针,缓存帧标号和随机数等相关数据 |
#### 5.1.4 void ssl_frame_maker(ssl_frame *frame, u8 *data, int data_len)
将相关数据打包进非对称双向认证包中。
| 变量名 | 输入/输出 | 说明 |
|:----:|:----:|:----:|
| in_frame | 输出 | 非对称双向认证包指针,指向需要被打包进非对称双向认证包的数据地址 |
| data | 输入 | 数据指针 |
| data_len | 输入 | 数据长度 |
#### 5.1.5 void zigbee_data_encrypt(data_frame *data, crypto_zdata_frame *zdata, bool (* SM4_encrypt)(u8 *key_origin, u32 key_len, u8 *in_origin, u32 in_len, u8 *out, u32 *out_len),QString en_key = "")
对数据进行对称加密。
| 变量名 | 输入/输出 | 说明 |
|:----:|:----:|:----:|
| data | 输入 | 待加密的数据指针 |
| zdata | 输出 | 待填充的对称加密数据包指针 |
| SM4_encrypt | 输入 | SM4加密算法函数指针 |
| en_key | 输入 | 若不提供密钥,则将使用默认密钥进行加密 |
#### 5.1.6 bool zigbee_data_dectypt(data_frame *data, crypto_zdata_frame *zdata,bool (* SM4_decrypt)(u8 *key_origin, u32 key_len, u8 *in, u32 in_len, u8 *out, u32 *out_len),QString en_key = "")
对数据进行对称解密,返回`true`代表解密成功,`false`代表解密失败,需要检查输入是否错误。
| 变量名 | 输入/输出 | 说明 |
|:----:|:----:|:----:|
| data | 输出 | 待解密的数据地址指针 |
| zdata | 输入 | 收到的对称加密数据包指针 |
| SM4_decrypt | 输入 | SM4解密算法函数指针 |
| en_key | 输入 | 若不提供密钥,则将使用默认密钥进行解密 |
### 5.2 协议认证/验证
#### 5.2.1 void HMAC_identify(device *self, device *node, hmac_frame *hframe, void (*sendTonode)(ZigbeeFrame &data), void (*SM3_HMAC)(u8 *key, int keylen,u8 *input, int ilen,u8 output[32] ))
对接收到的HMAC包进行数据认证。
| 变量名 | 输入/输出 | 说明 |
|:----:|:----:|:----:|
| self | 输出 | 接收方的`device`数据结构指针,缓存帧标号和随机数等相关数据 |
| node | 输出 | 发送方的`device`数据结构指针,缓存帧标号和随机数等相关数据 |
| hframe | 输入 | 接收到的对称双向认证包指针 |
| sendTonode | 输入 | 发送数据函数的指针 |
#### 5.2.2 bool data_frame_verify(data_frame *frame)
对数据帧进行验证,返回`true`代表包有效,`false`代表包损坏。
| 变量名 | 输入/输出 | 说明 |
|:----:|:----:|:----:|
| frame | 输入 | 待检验的数据帧指针 |
#### 5.2.3 void HMAC_changeVerifykey(u8 key[16], device* self, device *node, void (*sendTonode)(ZigbeeFrame &data),bool (* SM4_encrypt)(u8 *key_origin, u32 key_len, u8 *in_origin, u32 in_len, u8 *out, u32 *out_len))
发送对称双向认证密钥更换指令包。
| 变量名 | 输入/输出 | 说明 |
|:----:|:----:|:----:|
| key | 输入 | 新密钥 |
| self | 输出 | 接收方的`device`数据结构指针,缓存帧标号和随机数等相关数据 |
| node | 输出 | 发送方的`device`数据结构指针,缓存帧标号和随机数等相关数据 |
| sendTonode | 输入 | 发送数据函数的指针 |
| SM4_encrypt | 输入 | SM4加密算法函数指针 |
### 5.3 工具
#### 5.3.1 uint16_t crc16_xmodem(const uint8_t *buffer, uint32_t buffer_length)
生成CRC16校验码。
| 变量名 | 输入/输出 | 说明 |
|:----:|:----:|:----:|
| buffer | 输入 | 需要生成校验码的数据指针 |
| buffer_length | 输入 | 需要生成校验码的数据长度 |
#### 5.3.2 bool bytecmp(u8 *a, u8 *b, u16 length)
将两个输入进行逐字比较,即`memcmp`,返回`true`代表完全一致,`false`代表存在差异。
| 变量名 | 输入/输出 | 说明 |
|:----:|:----:|:----:|
| a | 输入 | 需对比数据A |
| b | 输入 | 需对比数据B |
| length | 输入 | 需对比数据长度 |