协议解析器
Go to file
Mentalflow 5b4cc11fc1
README: Bump to 1.0.2.
2024-03-03 18:52:50 +08:00
.github/workflows CMakeLists and Workflow: optimize runtime gemeration. 2024-02-02 01:58:59 +08:00
3rdparty SerialPort and ZigBee: Use QThread and bump to 1.0.2. 2024-03-03 18:45:02 +08:00
app_source SerialPort and ZigBee: Use QThread and bump to 1.0.2. 2024-03-03 18:45:02 +08:00
documents/pictures READEME: Update. 2024-02-25 00:08:01 +08:00
scripts CMakeLists and Workflow: optimize runtime gemeration. 2024-02-02 01:58:59 +08:00
.gitignore Update workflow and bug fix. 2024-01-13 04:40:49 +08:00
.gitmodules Workflow:bump to Qt 6.5.3. 2024-02-01 00:05:13 +08:00
CMakeLists.txt SerialPort and ZigBee: Use QThread and bump to 1.0.2. 2024-03-03 18:45:02 +08:00
LICENSE Initial commit 2024-01-12 19:32:59 +08:00
README.md README: Bump to 1.0.2. 2024-03-03 18:52:50 +08:00

README.md

ProtocolParser

Logo

ProtocolParser 协议解析器 V1.0.2

一、介绍

这是一个用于与DKY的THM3682实验箱搭配使用的上位机协议解析器具体协议如「通讯协议解析」所示后续功能使用说明待补充。

主界面(浅色) 主界面(深色)

主界面(浅色/深色模式)

二、通讯协议解析

1. 概述

本协议是一套运行在应用层的,用于端到端通讯的轻量级安全协议,支持非对称/对称的双向、单向认证及加密通讯。该协议的认证和加密通讯由SM系列SM2、SM3、SM4算法保障安全性较强可抵御重放攻击。协议易拓展数据包采用二进制传输适合在多种窄带宽场景下使用目前已在嵌入式、ARM、X86/64平台上测试通过

2. 密钥

本协议采用两类密钥一是用于双向认证的SM2公私钥对二是用于对称双、单向认证和加密传输的SM4密钥。

  • SM2密钥长度为公钥64Bytes512Bits私钥32 Bytes256Bits。密文长度为明文长度再加上96Bytes。签名长度为64Bytes被签名数据内包含随机数盐值因此对同一数据签名的结果不相同
  • SM4密钥长度为16Bytes128Bits

3. 认证流程

3.1 非对称双向认证

由于SM2算法会占用一部分资源非对称双向认证一般用于性能较好的终端如性能较强的单片机、树莓派或者PC等设备之间的认证性能较差的单片机将会出现死机等预料外情况板载硬件密码算法模块除外

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信息可不加密采用明文传输因为后续传输的数据包均为加密数据包

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 节点地址,仅在此帧被服务器进行路由时使用,
用以标记该帧由哪个节点最先产生
id 2 帧标号,用以与rand_num共同唯一标记一个帧,防止重放攻击
length 2 帧长度包括帧头最大为65535个Bits
reset_num 1 重置标记位,用以告知对方在接收此帧后,
下一帧的帧标号将归零,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):用以生成基础帧数据结构,numdata部分长度,使用时请注意强制类型转换

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):用以生成数据帧数据结构,numdata部分长度,使用时请注意强制类型转换

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_LENSM4默认填充长度固定为16Bytes
  • 宏定义DIGI_ENV_PREFIX_LEN数字信封包头长度固定为4Bytes
  • 宏定义DIGI_ENV_SESSION_KEY_LEN数字信封密态会话密钥长度固定为112Bytes
  • 宏定义DIGI_ENV_HEAD:数字信封包头,固定为0xAAAB
  • 宏定义new_digi_env(num):用以生成数字信封数据结构,numdata部分长度,使用时请注意强制类型转换

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):用以生成非对称双向认证包数据结构,numdata部分长度,使用时请注意强制类型转换

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, bool use_real_cbc),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, bool use_real_cbc),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, bool use_real_cbc))

发送对称双向认证密钥更换指令包。

变量名 输入/输出 说明
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 输入 需对比数据长度