EMS 知识库
首页 / knowledge/代码解析/common

simple_encryption.c

源文件路径:common/simple_encryption.c 所属模块:common(通用工具库) 代码行数:73

1. 文件职责

本文件实现 EMS 自有的轻量对称流式加解密算法:用一个 32 位「运行密钥」的低 8 位(实际取 key >> 8 的低 8 位)作为密钥流字节与明文异或,并在每个字节后用线性同余式结合刚产生的密文字节递推密钥,从而形成对「上一明文字节」自同步的字节流密码。它处在网络协议栈的数据域封装/解封装环节:发送侧 midware/ethernt/ethernet.c 在拼好 7 字节帧头与 JSON 数据域后调用 Encrypt 混淆数据域并重算 Modbus CRC;接收侧 net/server.c 在 CRC 校验通过后调用 decrypt 还原数据域,再交给 JSON 解析。仅当服务器配置为 NET_PRIVATE_ENCRYPTED(自建/测试服务器)时启用;正式云端走 TLS 通道时不走本算法。函数由调用方任务同步执行,无状态、无全局变量,因此可重入,但加密与解密双方必须严格同步(任一字节错位会导致其后全部字节错乱)。

2. 关键数据结构与宏

名称类型说明
SIMPLE_KEY_C1宏(来自头文件)26529,密钥递推乘数。奇数,使 x → x*C1 模 2^32 为双射
SIMPLE_KEY_C2宏(来自头文件)87282,密钥递推加数
SIMPLE_KEY_CRYPTKEY宏(来自头文件)42529(0xA621),密钥初值。其第 8..15 位为 0xA6,故首字节的密钥流字节恒为 0xA6
key局部 int32 位运行密钥状态,加/解密各自从 SIMPLE_KEY_CRYPTKEY 起算
PrevBlock局部 char(仅 decrypt)保存当前密文字节,用于在就地解密时先读后写并参与密钥递推

本文件不含结构体、枚举、联合体、位域,也不使用任何全局变量;无静态数据。

3. 函数清单

函数名作用域说明
Encrypt全局对 len 字节做流式加密:dst[i] = src[i] ^ (key>>8),随后 key = (key + (signed char)dst[i]) * C1 + C2(第 29 行)
decrypt全局对 len 字节做流式解密:先存 PrevBlock = src[i],再 dst[i] = src[i] ^ (key>>8),随后用 PrevBlock 递推密钥(第 56 行)

共 2 个全局函数,无 static 函数、无函数指针。两者互为逆运算。

4. 核心逻辑解析

本文件无状态机、无定时器、无 uCOS-II 原语,是纯字节循环。

4.1 加解密算法与公式

设明文 P[i]、密文 C[i]、运行密钥 K[i](int,32 位)、初值 K[0] = SIMPLE_KEY_CRYPTKEY = 42529 = 0x0000A621。

加密(第 38-42 行):

for i = 0 .. len-1:
    C[i] = (char)( P[i] ^ (K[i] >> 8) )                     // 密钥流字节 = (K[i]>>8) 的低 8 位 = K[i] 的第 8..15 位
    K[i+1] = (K[i] + (signed char)C[i]) * 26529 + 87282      // 32 位有符号 int 运算

解密(第 66-71 行):

for i = 0 .. len-1:
    Prev = C[i]
    P[i] = (char)( C[i] ^ (K[i] >> 8) )
    K[i+1] = (K[i] + (signed char)Prev) * 26529 + 87282

两式中的 (signed char) 强制转型是关键:无论编译器把裸 char 当作有符号还是无符号(ARM Compiler 默认裸 char 为无符号),递推中使用的都是密文字节的有符号值 [-128, 127],保证收发两端递推一致。K[i] >> 8 只取低 8 位参与异或,因此算术右移与逻辑右移在 i < 32 时结果相同,不构成平台差异。

算法性质:

4.2 输入校验与边界

两个函数在开头都只做一项检查(第 35-36 行、第 63-64 行):

if (len < 0) return;      // 不解密也不报错,静默返回

4.3 协议帧格式与加密位置

EMS 与服务器之间的帧由 net/ems.c:333-350 的 fill_common_data 拼装,布局为:

偏移长度内容是否被本算法加密
01协议头 EMS否
11cmdType(REQUEST_COMM / RESPONSE_COMM)否
21instr 命令字否
3-420x00 0x00 保留否
5-62数据域长度(大端)否
7N数据域(JSON 文本)是
7+N2Modbus CRC16(大端,覆盖 [0, 7+N))否(加密后重算)

加密区间即 [7, 总长-2),长度 = 总长 - 9,与调用式 msg->u32Len - 9 吻合。发送侧流程(ethernet.c:576-585、:791-800):

  1. if (get_server_data_encryption_flag() == NET_PRIVATE_ENCRYPTED) 判定通道加密;
  2. if ((msg->u32Len > 9) && (g_t_app_info.u8_app_connect_flag == FALSE)) 额外要求长度合法且未处于 APP 直连模式;
  3. Encrypt(&msg->pu8Info[7], &msg->pu8Info[7], msg->u32Len - 9);
  4. GetCrcCode_Modbus(msg->pu8Info, msg->u32Len - 2) 重算 CRC 并写回最后两字节。

接收侧流程(server.c:6404-6418):先 GetCrcCode_Modbus 校验整帧,再判断 NET_PRIVATE_ENCRYPTED 且 l_u32PackLen > 9 且非 APP 直连,然后 decrypt 就地还原数据域,最后 strstr / JSON 解析。

4.4 超时重试与任务配合

本文件不参与超时/重试:加密后的帧交给以太网/Wi-Fi 发送任务,CRC 由对端校验,重传由上层(net/ems.c 的应答等待与重发计数、midware/ethernt/ethernet.c 的发送循环)负责。本文件没有自己的定时器、信号量或消息邮箱,调用即返回,耗时与 len 成正比(字节循环,无查表,约每字节十几次整数运算)。

5. 对外接口

本文件导出(供谁调用)

本文件调用的外部函数/变量

6. 依赖关系

7. 备注与风险点

  1. 有符号整型溢出(UB):第 41、70 行的 (key + (signed char)dst[i]) * 26529 + 87282 在数十个字节内即会超出 int32_t 范围。C 标准中有符号溢出是未定义行为,编译器有权据此优化(如假设不会溢出而消去某些分支)。依赖 ARM 的补码回绕行为在实践中「能用」,但换编译器/开高优化等级有理论风险;建议显式改为 uint32_t 运算。
  2. len < 0 静默返回,无返回值:函数返回 void,上层无法区分「解密成功」与「因 len 非法而未解密」。若调用点算出负长度(l_u32PackLen - 9 在 l_u32PackLen < 9 时),未解密的密文会被当作明文 JSON 解析,且无任何日志可查。虽然 server.c:6412 已用 l_u32PackLen > 9 做了前置保护,但保护在调用方而非被调方,属脆弱设计。
  3. 无输出缓冲长度参数:dst 的容量完全由调用方保证,函数内部无任何越界防护;一旦 len 计算错误即静默踩内存。
  4. 密钥强度极低且全机共用:三个宏硬编码(26529 / 87282 / 42529),密钥流只有 8 位有效状态参与异或,且递推是公开线性关系。这只能算「混淆/OTP 式扰码」,不构成密码学强度;对固件逆向者可秒解。用于自建/测试服务器尚可,若被误用于正式通道则是安全隐患。
  5. 错误扩散无界:由于密钥递推依赖前一个密文字节,任何一个字节的传输错误会导致从该字节起后续全部解密失败(而非只错一个字节)。与「逐字节独立加密」的直觉相反,这一点在协议设计文档中通常不会写明,排障时容易误判为服务器下发数据错误。
  6. 命名与实现风格倒退:加密函数首字母大写、解密函数全小写(Encrypt / decrypt),无 const 修饰输入,无 u8/s32 等工程统一类型(用裸 char / int),与 common 目录其余文件的风格不一致;(signed char) 的关键转型缺少任何注释说明其必要性,后人「优化掉」这个转型就会立刻破坏收发一致性。
  7. 未使用的注释模板:函数头注释(第 18-28 行、第 45-55 行)保留了 @Brief、@Input、@Output、@Return 全空白的模板,@Name 写的是函数签名副本,属无效注释噪声。
  8. 有效密钥空间远小于 32 位:参与异或的只有 K[i] 的第 8..15 位共 8 位,其余 24 位状态只影响后续字节的递推;即使分析者无法反推 K[0],已知明文也能逐字节恢复全部密钥流字节。属算法层面的先天弱点,记录备查。