4. SM3密码杂凑算法原理
SM3,说白了就是咱们国家自己的SHA-256。2010年发布,2016年成为国标。我在做某款安全芯片的固件时,客户点名要用SM3做完整性校验——嗯,从那以后我就把这套流程刻在脑子里了。
4.1 SM3算法结构
SM3的结构其实不复杂。它接收任意长度的输入,输出固定256位的摘要。整个流程分三步:
- 消息填充 — 把输入补成512位的整数倍
- 迭代压缩 — 每512位一块,一块一块地算
- 输出摘要 — 最后8个32位字拼成256位结果
我习惯把SM3比作一台绞肉机。你塞进去一整头牛(任意长度消息),出来的是固定重量的肉馅(256位摘要)。而且只要换一头牛,肉馅就完全不一样。
核心参数速查:
- 消息分组长度:512位(64字节)
- 摘要长度:256位(32字节)
- 字长:32位
- 压缩轮数:64轮
4.2 消息填充
填充这一步,很多新手会栽跟头。我当年第一次实现SM3时,就因为在填充边界条件上少处理了一个字节,导致和标准向量对不上,查了整整两天。
填充规则其实就三条:
- 先补一个"1" — 在消息末尾加一个比特1
- 再补若干个"0" — 直到长度模512等于448
- 最后补64位长度 — 原始消息的比特长度,大端序
我的经验:在嵌入式环境里,消息长度通常用字节数表示。记得把字节长度乘以8转成比特长度再填充。我曾经见过有人直接填字节长度,结果摘要完全不对。
举个例子。假设输入消息是"abc"(3字节,24比特):
原始消息: 01100001 01100010 01100011 (3字节)
补"1": 01100001 01100010 01100011 1
补"0": 后面补423个0,使总长度=448
补长度: 最后64位填 0x00000000 00000018 (24的十六进制)
最终分组: 512位,正好一块
4.3 迭代压缩
迭代压缩是SM3的核心。每512位一块,经过64轮压缩,更新8个32位工作寄存器(V0~V7)。
初始化向量IV是固定的:
V0 = 0x7380166F
V1 = 0x4914B2B9
V2 = 0x172442D7
V3 = 0xDA8A0600
V4 = 0xA96F30BC
V5 = 0x163138AA
V6 = 0xE38DEE4D
V7 = 0xB0FB0E4E
每一轮压缩函数包含三个关键操作:
- 消息扩展 — 把16个字扩展成132个字(W0~W67 + W'0~W'63)
- 置换函数 — P0和P1,做循环移位和异或
- 布尔函数 — FF和GG,根据轮数选择不同逻辑
注意:SM3的布尔函数在16轮和48轮处会切换。FF在0~15轮是异或,16~63轮是多数表决。GG在0~15轮是异或,16~63轮是择多。这个切换点很容易搞混。
压缩一轮的伪代码大概长这样:
// 假设SS1、SS2、TT1、TT2是临时变量
// A~H是8个32位工作寄存器
SS1 = ((A <<< 12) + E + (Tj <<< j)) <<< 7
SS2 = SS1 ^ (A <<< 12)
TT1 = FFj(A, B, C) + D + SS2 + W'j
TT2 = GGj(E, F, G) + H + SS1 + Wj
D = C
C = B <<< 9
B = A
A = TT1
H = G
G = F <<< 19
F = E
E = P0(TT2)
64轮跑完后,把当前块的结果和上一块的V值异或,作为下一块的初始值。最后一块处理完,8个V值拼起来就是256位摘要。
4.4 SM3与SHA-256对比
很多人问我:SM3和SHA-256到底有啥区别?我一般这么回答:结构上几乎一样,但细节上处处不同。
| 对比项 | SM3 | SHA-256 |
|---|---|---|
| 输出长度 | 256位 | 256位 |
| 分组长度 | 512位 | 512位 |
| 压缩轮数 | 64轮 | 64轮 |
| 消息扩展 | 16→132字 | 16→64字 |
| 布尔函数 | FF/GG(16轮切换) | Ch/Maj(全程固定) |
| 循环移位 | 多种移位量 | 固定右移/循环右移 |
| 安全强度 | 128位(抗碰撞) | 128位(抗碰撞) |
从安全角度看,两者都在128位安全强度级别。但SM3的消息扩展更复杂(132个字 vs 64个字),扩散性理论上更好。我在做安全评估时,通常认为SM3和SHA-256在工程上是等价的——选哪个主要看合规要求。
嵌入式选型建议:
- 国内项目、国密合规:必须用SM3
- 国际项目、兼容性优先:用SHA-256
- 资源受限的MCU:两者开销差不多,选硬件加速支持的
嗯,SM3的原理就这些。结构清晰,实现起来也不复杂,但细节处容易踩坑。我个人建议:第一次实现时,一定用标准测试向量逐轮验证,别等到集成时才发现问题。