SM3 算法原理快速入门

做国密改造绕不开 SM3。说白了,它就是我国自主设计的杂凑函数,杂凑值长度 256 比特。你可能会问:**为什么不用现成的 SHA‑256?** 合规要求只是一方面,另一方面,SM3 在软件实现和硬件实现上都有一些独特的设计细节,实际改造时常常会遇到“看上去一样,调起来就坑”的地方。

我个人习惯先把算法结构拆开看,再对比它和 SHA‑256 到底差在哪。这一节你就跟着我走一遍 SM3 的核心理念,以后看 gmssl 源码就不会晕了。

SM3 采用 Merkle‑Damgård 结构,输出 256 比特。与 SHA‑256 同属 MD 家族,但压缩函数、消息扩展、初始值均有不同。

杂凑函数结构:Merkle‑Damgård 骨架

SM3 的结构其实是经典 MD 结构的变体。整个过程可以概括为三步:

  1. 消息填充:在消息末尾加 1 个比特 1,再加若干个 0,最后附加原始消息长度的 64 位大端表示,使总长度是 512 的整数倍。
  2. 分组处理:每 512 比特(64 字节)为一组,每组独立送入压缩函数。
  3. 迭代压缩:初始向量 IV(8 个 32 位字)与第一组压缩,输出新的中间值,再与下一组压缩,直到最后一组输出最终杂凑值。

下面的 SVG 图展示了这个流程,我建议你把它抄到笔记本上——每次做 SM3 相关开发时扫一眼就能回忆起整体路径。

SM3 Merkle‑Damgård 结构 消息输入 消息填充 512 位分组 初始 IV 压缩函数 CF (64 轮) 下一组 (若有) 256 位杂凑值 关键点 • 填充方式与 SHA‑256 相同 • 每组进入压缩函数前 需做消息扩展 (W0~W67)

与 SHA‑256 的异同

我知道很多人第一反应是:“反正都是 256 位,直接替换不就行?” 呃,不行。压缩函数细节差很多。我曾经在一个国密改造项目里试图用 SHA‑256 的硬件模块直接替换成 SM3,结果发现轮常数、布尔函数、循环移位量全部不一样,必须单独设计逻辑。下面这张表可以帮你快速对比:

对比项 SM3 SHA‑256
输出长度 256 比特 256 比特
消息块大小 512 比特 512 比特
压缩轮数 64 轮 64 轮
消息扩展方式 W0~W15 直接取自消息块,W16~W67 通过 16 步递推生成 W0~W15 直接取自消息块,W16~W63 通过 16 步递推生成
压缩函数特点 使用 3 个不同布尔函数(GG0、GG1、FF0、FF1) 使用 2 个布尔函数(Ch、Maj)
初始向量 IV 7380166f 4914b2b9 ... 6a09e667 bb67ae85 ...
循环移位量 部分轮次使用 7、8、12 等 固定使用 2、13、22 等
实战小技巧: 如果你在 OpenSSL 里同时支持 SM3 和 SHA‑256,可以用一个统一的消息填充函数,因为填充规则完全一样。压缩函数部分则必须各自实现独立的状态机。

安全强度分析

SM3 的设计目标就是 256 比特的安全等级。从抗碰撞性来看,目前公开文献中最好的攻击也只能达到大约 2248 的复杂度,远远高于 2128 的实践安全阈值。说白了,你不用担心它在碰撞攻击上出问题。

我印象比较深的是 2017 年左右,有指出对 SM3 的减轮版本(比如 16 轮)可以实现快速碰撞,但完整的 64 轮至今没有实质性突破。相比之下 SHA‑256 经历了更长时间的密码分析和实践检验,两者在安全强度上旗鼓相当。**但是请注意**,SM3 的布尔函数设计对某些类型的代数攻击有天然抵抗性,这是它的一个小优势。

曾经掉过的坑: 某次合规安全检查,对方要求“必须使用 SM3,不能用 SHA‑256”。我心想既然安全强度一样,干脆在软件层用 SHA‑256 算法库直接改个 SM3 的表?结果压缩函数输出不一致,杂凑值对不上。千万不要试图“近似替换”——SM3 的常数和布尔函数是严格定义的,差一个比特都不行。

典型应用场景

在实际国密改造中,你一定会遇到下面几种情况:

  • SM2 数字签名中的杂凑层:SM2 签名的第一步是计算 Z 值(ZA = SM3(ENTLA || IDA || a || b || xG || yG || xA || yA)),然后再对消息 M 做 SM3(ZA || M)。如果你忘了把椭圆曲线参数和用户标识拼进去,签出来的东西根本验不过。
  • 消息认证码 (MAC):直接用 SM3 拼接密钥和消息?那不安全,容易受长度扩展攻击。建议使用 HMAC‑SM3 结构,国标里有明确的标准。
  • 随机数生成与密钥派生:在 KDF 里把 SM3 当单向函数用,生成会话密钥或加密密钥。我习惯配合 SM3_CTX 复用上下文,避免重复初始化带来的性能开销。
  • 完整性校验:文件校验、固件签名、日志验真,都可以直接用 SM3 摘要。注意和 SHA‑256 的结果长度一样,但内容不同,升级时务必更新校验逻辑。

好了,SM3 的原理和特性和你说清楚了。下一回碰到 SHA‑256 替换成 SM3 的需求,你就能从容地拆开压缩函数,看看哪一步该改、哪步可以复用。嗯,设计时多留一个心眼:填充逻辑可以共享,压缩逻辑必须独立。