SM3密码杂凑算法:从原理到固件实战
SM3,说白了就是咱们国标里的“杂凑算法”。很多人叫它哈希算法,其实一回事。我最早接触SM3是在一个电力行业的固件升级项目里,甲方明确要求必须用国密,不能用SHA-256。那时候我还在想,这俩到底差在哪儿?后来踩了几个坑,才算真正搞明白。
这一章,咱们就把它彻底说透。
SM3算法原理与流程
SM3的输出长度是256位,也就是32字节。它的核心思想很简单:把任意长度的消息,通过填充、迭代压缩,最终变成一个固定长度的摘要。
整个流程可以分成四步:
- 消息填充:补位,让消息长度变成512的倍数
- 初始值设定:8个32位寄存器,赋初值
- 迭代压缩:64轮循环,每轮用消息扩展和压缩函数
- 输出摘要:8个寄存器的值拼接成256位
核心要点:SM3的压缩函数采用了“Feistel结构”的变体,每轮用两个消息字参与运算。这一点和SHA-256不太一样,后面会细说。
我习惯把SM3的迭代过程想象成一个“绞肉机”。消息块进去,经过64轮搅拌,出来一堆看起来毫无规律的比特。嗯,这个比喻虽然糙,但道理不糙。
消息填充与迭代压缩
消息填充这块,我见过不少新手栽跟头。咱们直接说规则:
- 先在消息末尾补一个“1”
- 然后补“0”,直到长度 mod 512 = 448
- 最后64位放原始消息的长度(单位是比特)
我曾经在一个项目里,因为填充时长度单位搞错了(用了字节而不是比特),导致摘要死活对不上。查了两天才发现,原来是填充的长度值少乘了8。这种错误,编译器不会报错,只有比对结果时才能发现。
迭代压缩是SM3的核心。它把512位的消息块扩展成132个32位字(W0~W67, W'0~W'63),然后做64轮压缩。
压缩函数里有两个关键操作:
- SS1, SS2:中间变量,由寄存器值和消息字计算
- TT1, TT2:临时变量,用于更新寄存器
每轮更新8个寄存器(A~H),64轮后输出最终值。我个人的理解是:这就像8个变量在64轮里互相“搅和”,每一轮都引入新的消息字,最终达到雪崩效应。
小技巧:调试SM3时,可以先用官方测试向量验证中间值。我习惯把每轮的A~H值打印出来,和标准结果逐轮比对。一旦某轮对不上,问题就锁定在那轮的消息扩展或压缩函数里。
SM3与SHA-256的对比分析
很多人问我:SM3和SHA-256到底谁更强?我的回答是:从安全强度看,两者旗鼓相当。但细节上确实有差异。
| 对比项 | SM3 | SHA-256 |
|---|---|---|
| 输出长度 | 256位 | 256位 |
| 消息分组 | 512位 | 512位 |
| 压缩轮数 | 64轮 | 64轮 |
| 消息扩展 | 生成132个字 | 生成64个字 |
| 布尔函数 | FF, GG(含异或) | Ch, Maj |
| 移位操作 | 循环左移+右移 | 右移为主 |
| 国标编号 | GB/T 32905-2016 | FIPS PUB 180-4 |
你看,SM3的消息扩展更复杂,生成了132个字,而SHA-256只生成64个。这意味着SM3每轮有更多的消息字参与运算,扩散性理论上更好。
另外,SM3的布尔函数FF和GG里引入了异或操作,而SHA-256用的是Ch和Maj。异或的非线性特性更强,对抵抗差分攻击有帮助。
但说实话,在实际工程中,两者的性能差异很小。我做过对比测试,在ARM Cortex-M4上,SM3比SHA-256慢大约10%~15%。这个差距在大多数场景下可以忽略。
我的建议:如果项目要求国密合规,直接用SM3。如果没有合规要求,SHA-256也完全够用。不要为了“哪个更安全”纠结,两者的安全余量都足够。
SM3在固件完整性校验中的应用
固件完整性校验,说白了就是确保你烧录进去的固件没被篡改。SM3在这里扮演的角色就是“指纹生成器”。
典型的流程是这样的:
- 编译生成固件二进制文件(.bin或.hex)
- 用SM3计算整个固件的摘要值
- 把摘要值存储在固件的固定位置(比如末尾或头部)
- 设备启动时,重新计算固件摘要,和存储的摘要比对
如果比对一致,说明固件没被改过。不一致,说明固件可能被篡改或损坏。
我在一个物联网网关项目里用过这个方案。当时遇到一个问题:固件里包含了摘要值本身,计算摘要时要不要包含它?
答案是:不要包含。否则就成了“自己算自己”,永远对不上。
我曾经犯过这个错误。第一次做固件校验时,我把整个固件文件(包括预留的摘要区)都拿去算SM3,结果算出来的摘要和存储的摘要永远不一致。后来才意识到,摘要区在计算时应该填0,算完后再把真实摘要写进去。
具体实现时,我习惯这样做:
- 在固件链接脚本里预留32字节的摘要区
- 编译后,用脚本计算固件(摘要区填0)的SM3值
- 把SM3值写入摘要区
- 设备启动时,同样把摘要区当0处理,重新计算并比对
另外,SM3还可以和签名算法配合使用。比如先用SM3算摘要,再用SM2签名。这样既能保证完整性,又能保证真实性——确保固件来自合法的发布者。
避坑指南:固件校验时,注意字节序。SM3输出是大端序,如果你的MCU是小端序,直接比对可能会失败。我习惯在比对前统一转成大端序,或者用memcmp逐字节比对。