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

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局部 s32snprintf 已写出长度累加值,实现「追加式」拼串

本文件不含结构体、枚举、联合体、位域;除上述宏与 snprintf 外无其它外部接口。

3. 函数清单

函数名作用域说明
alpha_generate_pwd全局校验参数与长度 → 计算 sum1/sum2 → 三段公式生成 8 位大写 HEX 密钥 → 强制末字节 \0,成功返回 TRUE(第 14 行)

共 1 个全局函数,无 static 辅助函数、无函数指针、无回调。整个算法在单函数内顺序执行完成。

4. 核心逻辑解析

本文件无状态机、无定时器、无 uCOS-II 原语;核心是「校验 → 两个和 → 三段线性组合」的纯计算流程。

4.1 参数与长度校验(第 16-29 行)

  1. if (input_str == NULL || output_str == NULL) → 打印一条 printf 后 return FALSE(第 16-19 行)。
  2. u32 input_str_len = strlen(input_str);(第 21 行)。
  3. if (input_str_len < INPUT_STR_LEN_MIN) → 打印后 return FALSE(第 22-25 行)。
  4. 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) 为末字符):

段字符数公式输出格式
part13sum1*122 + sum2*33 + S(5)*31 + S(2)*919 + S(1)*918 + 2025,再 & 0xFFF"%03X" 大写 HEX
part22sum1*345 + sum2*66 + S(6)*42 + S(3)*616 + S(1)*147 + 2023,再 & 0xFF"%02X" 大写 HEX
part33sum1*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 中的流程:

  1. 第 188 行 char password_buf[ALPHA_GENERATE_PWD_LENGTH] = {0};
  2. 第 243-247 行:若模组 u8_tcp_tls_support_flag == TRUE(易联认为支持 TLS 即支持一机一密),memset 清空缓冲后调 alpha_generate_pwd(get_system_sn(), password_buf);否则(不支持一机一密)用默认密码 AT_DEFAULT_PWD。
  3. 第 248-255 行:for (i = 0; i < 3; i++) 最多重试 3 次把密钥写入模组(set_e_linker_key_algorithm),任一次成功即 break。
  4. 第 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. 对外接口

本文件导出(供谁调用)

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

6. 依赖关系

7. 备注与风险点

  1. printf 打印 NULL 指针:第 17 行 printf("input_str:%s or output_str:%s is NULL", input_str, output_str); 在参数为 NULL 时把 NULL 传给 %s,C 标准下属未定义行为(虽然多数嵌入式 libc 会打出 (null) 或空串)。正确做法是改为固定字符串或打印指针值 %p。
  2. 长度上限校验不返回失败:第 27-29 行只 printf 而缺少 return FALSE,导致 SN 超过 18 字节时函数仍继续生成密钥。这与文件头注释「输入的字符串长度必须为 11~18 字节」的承诺不一致,也与同一个校验块中「过短就返回失败」的处理不对称,疑为漏写 return。
  3. sum1 的循环上界与宏脱钩:第 32 行硬编码 i < 11,而 INPUT_STR_LEN_MIN 也是 11;若将来把最小长度改成别的值,sum1 仍固定累加前 11 个字符,公式与 Excel 的一致性会被静默破坏。
  4. snprintf 返回值未做负值防护:第 43 行 index = snprintf(...),若 snprintf 返回负值(编码/输出错误),后续 ALPHA_GENERATE_PWD_LENGTH - index 会大于缓冲长度,snprintf 的 size 保护失效,第 48 行起可能在 9 字节缓冲外写数据。实际参数下(size 9、输出 3 字符)不会触发,属理论风险;显式判负更稳妥。
  5. 无输出长度参数、无失败回填:函数不接收 output_str 的容量,完全依赖调用方使用 ALPHA_GENERATE_PWD_LENGTH 定义缓冲。若调用方用更小的缓冲,snprintf 的 size 参数仍写成 9,会真实溢出。
  6. 硬编码的算法常量无来源说明:122 / 33 / 31 / 919 / 918 / 2025、345 / 66 / 42 / 616 / 147 / 2023、216 / 99 / 54 / 313 / 581 / 2030 共 18 个魔法数字只靠 Excel 注释间接解释,且第三段注释已损坏。若密钥算法在服务端/云端实现,任一侧改动都会造成设备无法接入,但工程内没有版本号或一致性自检(如已知 SN→已知密钥的测试向量)。
  7. u32 input_str_len 与 s32/int32_t 混用:第 21 行用 u32,第 32、36 行循环变量分别用 int32_t,第 41 行下标为 u32 表达式。当 input_str_len 小于 7 时下标会下溢为极大 u32,导致越界读;当前被「长度 < 11 即返回」挡住,但依赖的是校验顺序而非类型安全。
  8. 大小写敏感与字符集假设:算法直接使用 char 的 ASCII 码值(sum1 += input_str[i]),未考虑 SN 含非 ASCII 字符或不同大小写的场景;若 SN 字符集改变(例如引入小写或数字前缀),生成的密钥会与服务端计算不一致,且无任何校验可发现。
  9. 调试输出留在量产代码中:三处 printf 未加 #if (DEBUG_...) 条件编译,与 net/、midware/ 目录普遍使用 #if (DEBUG_TCP_CLIENT_STATE == 1) 包裹的风格不一致,会持续占用串口带宽。