md5.h
源文件路径:
common/md5.h所属模块:common(通用工具库) 代码行数:60
1. 文件职责
本文件是 RSA Data Security 原始 MD5 参考实现的配套头文件(保留 1990 年版权声明),声明 MD5 消息摘要算法所需的上下文结构体 MD5_CTX 与 4 个函数原型。它在 EMS 里承担两类用途:一是网络层生成「动态登录密码 / 特服指令校验值」(net/ems.c、net/server.c),二是固件升级前后对 Boot、EMS 固件镜像做完整性校验(net/ems_boot_handle.c、net/ems_download.c、net/upd_download.c)。本文件不含实现、不参与数据流计算,只把摘要上下文的内存布局(位计数、输入缓冲、摘要缓冲)固定下来,供调用方在栈上分配 MD5_CTX 局部变量。全部代码行中有 38 行是版权注释,实际内容仅 15 行。
2. 关键数据结构与宏
| 名称 | 类型 | 说明 |
|---|---|---|
UINT4 | 类型别名 | typedef unsigned long int UINT4;(第 41 行)。GD32F407 为 32 位 ARM,unsigned long 恰好 4 字节,与算法要求一致;移植到 64 位平台会失效 |
MD5_CTX | 结构体 | MD5 计算上下文(第 44-49 行):i[2](已处理位数 mod 2^64 的低/高 32 位)、buf[4](A/B/C/D 中间状态与暂存)、in[64](待处理分组缓冲)、digest[16](MD5Final 后的最终摘要,小端按字展开) |
本文件不定义枚举、联合体、位域,也没有除 include guard 外的宏常量——实际上连 include guard 都没有(见第 7 节风险 1)。MASK 之类的辅助宏定义在 md5.c 内部而非本头文件。
3. 函数清单
| 函数名 | 作用域 | 说明 |
|---|---|---|
MD5Init | 全局(原型声明) | 初始化上下文,写入 4 个魔数初值,定义于 md5.c:82 |
MD5Update | 全局(原型声明) | 追加数据(支持任意次分段调用),定义于 md5.c:93 |
MD5Final | 全局(原型声明) | 填充分组 + 追加位长度 + 输出 16 字节摘要,定义于 md5.c:122 |
Transform | 全局(原型声明) | 单分组压缩函数(64 步 FF/GG/HH/II),定义于 md5.c:155(但实际为 static,见第 7 节风险 2) |
共 4 个原型声明,全部对应 md5.c 中的定义;本头文件没有 static 函数、没有宏函数、没有函数指针。
4. 核心逻辑解析
本文件是纯声明文件,无可执行逻辑、无状态机、无定时器、不涉及 uCOS-II。要点在接口契约:
4.1 调用契约(三段式)
标准用法为 MD5Init → MD5Update(可多次) → MD5Final,MD5_CTX 由调用方在栈上分配并传入指针。工程内 5 处调用点全部遵循该顺序:
net/ems.c:388-391:对"ALPHA" + SN求摘要,取digest[12..15]4 字节格式化为 8 位大写 HEX 作为动态登录密码。net/server.c:2049-2058:把 SN、目标 IP、端口、加密口等 6 段字符串依次MD5Update,摘要全 16 字节展开为 32 位 HEX,与云端下发的l_s8_temp_md5比对,通过后才落盘服务器地址/端口配置。net/ems_boot_handle.c:101-127:对新旧 Boot 镜像分别求摘要,与预置的s_u8_old_boot_md5/s_u8_new_boot_md5用memcmp(..., 16)比对。net/ems_download.c:127-157:按 1024 字节一块流式喂入固件,逐块IWDG_Feed(),末尾处理余数块。net/upd_download.c:397-399:一次性对 SDRAM 中整份固件求摘要。
4.2 摘要呈现格式
调用方约定把 md5.digest[0..15] 按存储顺序逐字节 sprintf("%02X") 拼成 32 字符大写 HEX(ems_download.c:159-161、server.c:2060-2061、upd_download.c:404-406)。这与 RFC 1321 的摘要文本表示一致(MD5Final 已把 buf[] 按小端拆字节存入 digest,见 md5.c:145-150)。net/ems.c 只截取后 4 字节是业务约定,不是算法约定。
4.3 结构体字段的读写分工
i[2]:MD5Init清零,MD5Update累加位计数并检测进位(md5.c:103-106),MD5Final读出作为长度尾。buf[4]:MD5Init写初值,Transform原地累加,MD5Final读出转digest。in[64]:MD5Update按mdi = (i[0] >> 3) & 0x3F定位写入位置并满 64 字节触发Transform。digest[16]:仅MD5Final写入,调用方只读。
因为 MD5_CTX 完全由调用方管理且各调用点使用局部变量,不同任务间不共享上下文,不存在跨任务的竞态。
5. 对外接口
本文件导出(供谁调用)
- 类型:
UINT4、MD5_CTX - 函数原型:
MD5Init、MD5Update、MD5Final、Transform - 调用方(
grep -rln "md5.h"):common/md5.c、net/ems.c、net/ems_boot_handle.c、net/ems_download.c、net/inv_download.c(仅 include,未实际调用任何 MD5 函数)、net/server.c、net/upd_download.c
本文件调用的外部函数/变量
- 无。本文件不 include 任何头文件(连
chip.h都不需要,因为只用了unsigned long/unsigned char内建类型)。这一点与common目录其它头文件(如crc.hincludechip.h)风格不同。
6. 依赖关系
- 依赖:无。零 include,纯 C 内建类型,可独立编译。
- 被依赖:
common/md5.c,以及网络层 5 个模块net/ems.c、net/ems_boot_handle.c、net/ems_download.c、net/inv_download.c、net/server.c、net/upd_download.c。工程 Keil 目标user/EMS_3_0.uvprojx中包含md5.c,参与编译。
7. 备注与风险点
- 没有 include guard:第 40-54 行直接是
typedef与函数声明,文件开头(第 1-38 行)全是注释,没有#ifndef __MD5_H__。当前工程无文件重复 include 它,所以未暴露问题;但一旦某个 .c 经两条路径引入md5.h,typedef unsigned long int UINT4;会重复定义(C99 起允许重复 typedef 且定义相同,MD5_CTX结构体重复定义则会直接编译报错)。属明显遗留隐患。 Transform声明与定义的作用域冲突:md5.h:54声明为全局void Transform(UINT4 *buf, UINT4 *in);,而md5.c:155定义为static void Transform(...)。由于全工程除md5.c自身外无任何地方引用Transform(grep -rn "\bTransform\b"零命中),链接期未报冲突,但头文件里的原型是死声明,且给调用者提供了「可以外部调用单分组压缩」的错误暗示。#include风格不统一:UINT4用内建类型而非chip.h的u32,导致common目录出现两套整数类型体系(u8/u16/u32与UINT4/unsigned char),阅读时需要心算等价关系。- 无
const限定:MD5Update(MD5_CTX *, char *inBuf, unsigned int inLen)的输入缓冲是非 const 的char *,迫使调用方对只读固件缓冲做强制转型(如(char *)MCU_START_ADDRESS、(char *)g_u8_fw_sdram),削弱了类型安全。 - 版权声明的义务:第 16-38 行的 RSA 许可要求任何提及本算法的材料标注 "RSA Data Security, Inc. MD5 Message Digest Algorithm"。本项目文档与产品说明中若未标注,属许可合规问题(非技术缺陷)。
- MD5 已不具抗碰撞性:用于动态密码与服务器配置校验时,MD5 只能防误改、不能防篡改;对固件完整性校验(
ems_boot_handle.c)而言这是安全强度上的已知弱点。