alpha_generate_pwd.c
源文件路径:
common/alpha_generate_pwd.c所属模块:common(通用工具库) 代码行数:58
1. 文件职责
本文件实现「一机一密」Wi-Fi 接入密钥的离线派生算法 alpha_generate_pwd:以设备序列号(SN)为输入,用三段定长线性组合公式各算出 3/2/3 个十六进制字符,拼成固定 8 位的密钥串。它处在无线模组配置流程的入口:midware/ethernt/wireless/wifi/e_linker_wifi.c 在检测到模组支持 TLS(即支持一机一密)时,用 get_system_sn() 生成密钥并反复尝试写入易联模组,写入成功后把密钥保存在 g_t_system_base_info.s8_e_link_password 中用于本地比对。本文件由 Wi-Fi 配置任务同步调用,纯计算、无状态、无全局变量、不访问硬件,也不依赖 uCOS-II。算法的权威定义是一份 Excel 公式(文件头第 9-13 行以注释保留),本文件是该公式的 C 语言移植。
2. 关键数据结构与宏
| 名称 | 类型 | 说明 |
|---|---|---|
INPUT_STR_LEN_MIN | 宏 | 11,输入字符串允许的最短长度;短于此长度直接返回失败(第 3 行) |
INPUT_STR_LEN_MAX | 宏 | 18,输入字符串允许的最长长度;超过只打印提示、不返回失败(第 4 行) |
RIGHT_DYN_CHAR_NUM | 宏 | 7,参与运算的「右侧动态字符窗口」宽度,即取 SN 末尾 7 个字符(第 6 行) |
ALPHA_GENERATE_PWD_LENGTH | 宏(来自头文件) | 9,输出缓冲长度(含 \0),即输出 8 个可见字符 |
sum1 | 局部 u32 | 输入字符串前 11 个字符的 ASCII 码之和(固定取 11 个,与真实长度无关) |
sum2 | 局部 u32 | 输入字符串末尾 7 个字符的 ASCII 码之和 |
part1 / part2 / part3 | 局部 u32 | 三段输出字符的数值;分别经 & 0xFFF、& 0xFF、& 0xFFF 截断后格式化为 3/2/3 位 HEX |
index | 局部 s32 | snprintf 已写出长度累加值,实现「追加式」拼串 |
本文件不含结构体、枚举、联合体、位域;除上述宏与 snprintf 外无其它外部接口。
3. 函数清单
| 函数名 | 作用域 | 说明 |
|---|---|---|
alpha_generate_pwd | 全局 | 校验参数与长度 → 计算 sum1/sum2 → 三段公式生成 8 位大写 HEX 密钥 → 强制末字节 \0,成功返回 TRUE(第 14 行) |
共 1 个全局函数,无 static 辅助函数、无函数指针、无回调。整个算法在单函数内顺序执行完成。
4. 核心逻辑解析
本文件无状态机、无定时器、无 uCOS-II 原语;核心是「校验 → 两个和 → 三段线性组合」的纯计算流程。
4.1 参数与长度校验(第 16-29 行)
if (input_str == NULL || output_str == NULL)→ 打印一条printf后return FALSE(第 16-19 行)。u32 input_str_len = strlen(input_str);(第 21 行)。if (input_str_len < INPUT_STR_LEN_MIN)→ 打印后return FALSE(第 22-25 行)。if (input_str_len > INPUT_STR_LEN_MAX)→ 只打印提示,不返回(第 27-29 行),函数继续用「后 7 字符」窗口计算。这是校验不对称的写法,功能上仍安全(下标均从末尾回溯,长度越长下标越大),但与「必须 11~18」的注释承诺不符。
4.2 两个累加和(第 31-38 行)
sum1 = Σ input_str[i] , i = 0 .. 10 // 固定前 11 个字符
sum2 = Σ input_str[len-1-j] , j = 0 .. 6 // 末尾 7 个字符(RIGHT_DYN_CHAR_NUM = 7)
注意 sum1 的循环上界硬编码为 11(第 32 行 i < 11),与 INPUT_STR_LEN_MIN 数值相同但未使用该宏,两者是偶然相等;当 len > 11 时,sum1 与 sum2 的字符区间会重叠(例如 SN 长度 16 时,下标 9、10 被计入两次),这是 Excel 公式的原始设计意图,不是笔误。
4.3 三段密钥生成公式(第 40-53 行)
记 L = input_str_len,窗口内位置以「从末尾数第 k 个字符」写作 S(k) = input_str[L-k](k 从 1 起算,S(1) 为末字符):
| 段 | 字符数 | 公式 | 输出格式 |
|---|---|---|---|
part1 | 3 | sum1*122 + sum2*33 + S(5)*31 + S(2)*919 + S(1)*918 + 2025,再 & 0xFFF | "%03X" 大写 HEX |
part2 | 2 | sum1*345 + sum2*66 + S(6)*42 + S(3)*616 + S(1)*147 + 2023,再 & 0xFF | "%02X" 大写 HEX |
part3 | 3 | sum1*216 + sum2*99 + S(7)*54 + S(4)*313 + S(1)*581 + 2030,再 & 0xFFF | "%03X" 大写 HEX |
代码中的下标写法为 input_str[input_str_len - 1 - (RIGHT_DYN_CHAR_NUM - n)],等价于 S(n)(第 41、46、51 行)。三段拼装使用 snprintf 追加:
index = snprintf(output_str, ALPHA_GENERATE_PWD_LENGTH, "%03X", part1); // 写 3 字符 + \0
index += snprintf(output_str+index, ALPHA_GENERATE_PWD_LENGTH-index, "%02X", part2); // size 6,写 2 字符 + \0
index += snprintf(output_str+index, ALPHA_GENERATE_PWD_LENGTH-index, "%03X", part3); // size 4,写 3 字符 + \0
最后一次写入在偏移 8 处落 \0,第 55 行再显式 output_str[ALPHA_GENERATE_PWD_LENGTH - 1] = 0; 做冗余保证。因此输出恒为 8 个大写十六进制字符(%X 非 %x),例如 A3F+1B+C07 形式。
4.4 与 Excel 权威公式的对应关系及注释缺陷
文件头第 9-13 行以注释形式保留了对应的 xlsx 公式(RIGHT(DEC2HEX(...),3) & RIGHT(DEC2HEX(...),2) & RIGHT(DEC2HEX(...),3)),本文件是它的 C 移植。三段代码与注释在前两段上逐系数一致(122/33/31/919/918/2025、345/66/42/616/147/2023);第三段的注释被写坏了:第 12 行写成 ...*216(CODE(MID(RIGHT(A2,7),1,1))+...)*99+...,丢掉了 *216 与括号之间的 +,成为 216(CODE(...) 这种非法写法,虽然其后半段系数(54、313、581、2030)与代码一致,但整条注释无法再被 Excel 解析验证。属注释与实现的可追溯性缺陷(代码本身正确)。
4.5 调用时序与落盘路径
midware/ethernt/wireless/wifi/e_linker_wifi.c 中的流程:
- 第 188 行
char password_buf[ALPHA_GENERATE_PWD_LENGTH] = {0}; - 第 243-247 行:若模组
u8_tcp_tls_support_flag == TRUE(易联认为支持 TLS 即支持一机一密),memset清空缓冲后调alpha_generate_pwd(get_system_sn(), password_buf);否则(不支持一机一密)用默认密码AT_DEFAULT_PWD。 - 第 248-255 行:
for (i = 0; i < 3; i++)最多重试 3 次把密钥写入模组(set_e_linker_key_algorithm),任一次成功即break。 - 第 337-341 行、第 354 行:把生成的密钥与
g_t_system_base_info.s8_e_link_password比对,不同则memcpy更新(该字段由数据层负责后续落盘)。
get_system_sn() 定义于 net/server.c:414-422,返回 16 字节的 SN 静态缓冲(static char s_s8_sn[17]),长度落在 11~18 区间内,因此实际调用不会触发长度校验分支。
5. 对外接口
本文件导出(供谁调用)
- 函数:
alpha_generate_pwd(第 14 行,声明于alpha_generate_pwd.h:8) - 调用方:
midware/ethernt/wireless/wifi/e_linker_wifi.c:247(唯一调用点,#include见第 20 行)
本文件调用的外部函数/变量
- 标准库:
strlen(第 21 行,来自string.h,经includes.h引入)、snprintf(第 43、48、53 行)、printf(第 17、23、28 行) - 无 EMS 其它模块的调用;
get_system_sn()由调用方在外部求值后作为参数传入
6. 依赖关系
- 依赖:
common/alpha_generate_pwd.h(唯一 include,第 1 行)→includes.h→stdio.h/string.h/stdlib.h/ucos_ii.h/gd32f4xx.h/chip.h(TRUE/FALSE来自chip.h:64的枚举)。本文件实际不需要 RTOS 或芯片头。 - 被依赖:
midware/ethernt/wireless/wifi/e_linker_wifi.c。工程 Keil 目标user/EMS_3_0.uvprojx中包含alpha_generate_pwd.c,参与编译。
7. 备注与风险点
printf打印 NULL 指针:第 17 行printf("input_str:%s or output_str:%s is NULL", input_str, output_str);在参数为 NULL 时把 NULL 传给%s,C 标准下属未定义行为(虽然多数嵌入式 libc 会打出(null)或空串)。正确做法是改为固定字符串或打印指针值%p。- 长度上限校验不返回失败:第 27-29 行只
printf而缺少return FALSE,导致 SN 超过 18 字节时函数仍继续生成密钥。这与文件头注释「输入的字符串长度必须为 11~18 字节」的承诺不一致,也与同一个校验块中「过短就返回失败」的处理不对称,疑为漏写return。 sum1的循环上界与宏脱钩:第 32 行硬编码i < 11,而INPUT_STR_LEN_MIN也是 11;若将来把最小长度改成别的值,sum1仍固定累加前 11 个字符,公式与 Excel 的一致性会被静默破坏。snprintf返回值未做负值防护:第 43 行index = snprintf(...),若snprintf返回负值(编码/输出错误),后续ALPHA_GENERATE_PWD_LENGTH - index会大于缓冲长度,snprintf的 size 保护失效,第 48 行起可能在 9 字节缓冲外写数据。实际参数下(size 9、输出 3 字符)不会触发,属理论风险;显式判负更稳妥。- 无输出长度参数、无失败回填:函数不接收
output_str的容量,完全依赖调用方使用ALPHA_GENERATE_PWD_LENGTH定义缓冲。若调用方用更小的缓冲,snprintf的 size 参数仍写成 9,会真实溢出。 - 硬编码的算法常量无来源说明:
122 / 33 / 31 / 919 / 918 / 2025、345 / 66 / 42 / 616 / 147 / 2023、216 / 99 / 54 / 313 / 581 / 2030共 18 个魔法数字只靠 Excel 注释间接解释,且第三段注释已损坏。若密钥算法在服务端/云端实现,任一侧改动都会造成设备无法接入,但工程内没有版本号或一致性自检(如已知 SN→已知密钥的测试向量)。 u32 input_str_len与s32/int32_t混用:第 21 行用u32,第 32、36 行循环变量分别用int32_t,第 41 行下标为u32表达式。当input_str_len小于 7 时下标会下溢为极大u32,导致越界读;当前被「长度 < 11 即返回」挡住,但依赖的是校验顺序而非类型安全。- 大小写敏感与字符集假设:算法直接使用
char的 ASCII 码值(sum1 += input_str[i]),未考虑 SN 含非 ASCII 字符或不同大小写的场景;若 SN 字符集改变(例如引入小写或数字前缀),生成的密钥会与服务端计算不一致,且无任何校验可发现。 - 调试输出留在量产代码中:三处
printf未加#if (DEBUG_...)条件编译,与net/、midware/目录普遍使用#if (DEBUG_TCP_CLIENT_STATE == 1)包裹的风格不一致,会持续占用串口带宽。