第3节 SM4对称密码算法原理归纳总结

SM4,说白了就是咱们国标里的分组密码。密钥长度128位,分组长度也是128位,一共跑32轮迭代结构。最早是给无线局域网用的,后来成了GB/T 32907标准。我记得第一次在HSM里集成SM4时,还特意跟AES比过一轮——两者轮数差了快一倍,但SM4的硬件逻辑其实更紧凑。

算法全貌:别说你只知道AES

SM4用的是广义Feistel结构,但不是传统那种左右交换。它每轮只更新输出中的一个字,剩下的三个字直接移位。我个人习惯这样理解:你有一串四元组 (X₀, X₁, X₂, X₃),每轮算出一个新字,然后整个四元组右移一位。32轮下来四个字全换过了。

解密和加密共享同一套轮函数,只是轮密钥的顺序反过来。嗯,这里要留心:硬件复用的时候,密钥扩展器要能逆向输出轮密钥。

核心参数清单
参数值
密钥长度128 bit
分组长度128 bit
算法结构广义Feistel(32轮)
轮函数非线性变换 T = S盒 + 线性L
轮密钥数32个(每个32 bit)

加密流程:一步一步跟着走

拿一个128位明文进来,先拆成四个32位字:(X₀, X₁, X₂, X₃)。轮函数 F 长这样:

F(X₀, X₁, X₂, X₃, rk) = X₀ ⊕ T( X₁ ⊕ X₂ ⊕ X₃ ⊕ rk )

每一轮:

  1. 用 X₁, X₂, X₃ 和轮密钥 rk 异或
  2. 丢进 T变换,先过S盒再过L线性置换
  3. 和 X₀ 异或得到新字
  4. 输出 (X₁, X₂, X₃, 新字)

重复32轮后,最后一轮输出的四元组就是密文。我刚开始写SM4软件模拟时,总把更新顺序搞反,结果解密出来全是乱码。后来养了个习惯——画个轮序号表贴屏幕上,再也不出错了。

避坑提示: 硬件实现时,32轮计算是串行的,但密钥扩展可以提前并行算完存到寄存器阵列里。我曾经在一版ASIC里贪图面积复用了轮函数单元,结果密钥扩展和加密打架,最后加了一级流水才搞定。

轮函数详解:S盒 + 线性L,缺一不可

T变换分两步:

  • S盒替换:把32位拆成4个字节,每个字节查8×8 S盒(固定表,共256项)。S盒是SM4唯一非线性来源。
  • 线性置换L:对S盒输出做一个32位的循环移位异或:
    L(B) = B ⊕ (B ≪ 2) ⊕ (B ≪ 10) ⊕ (B ≪ 18) ⊕ (B ≪ 24)
    移位方向全部左移。

你想想看,这个L其实就是个最大距离可分码的生成矩阵,扩散效果特别好。我在FPGA里直接把它写成多级XOR树,一个周期出结果。

代码片段:线性L的C实现
uint32_t L(uint32_t B) {
    return B ^ (B << 2) ^ (B << 10) ^ (B << 18) ^ (B << 24);
}

注意:移位后要 & 0xFFFFFFFF,但uint32_t自动溢出就行。

密钥扩展:别小看那32个轮密钥

密钥扩展用的几乎是同一套轮函数,区别只有两点:

  • 输入的是系统密钥 MK₀~MK₃,先跟固定常数 FK₀~FK₃ 异或得到初始状态 K₀~K₃
  • T变换里的L改成 L':移位循环位数不同
    L'(B) = B ⊕ (B ≪ 13) ⊕ (B ≪ 23)
  • 每轮还加一个轮常数 CKᵢ(固定表)

流程:

Kᵢ₊₄ = Kᵢ ⊕ T'(Kᵢ₊₁ ⊕ Kᵢ₊₂ ⊕ Kᵢ₊₃ ⊕ CKᵢ)
rkᵢ = Kᵢ₊₄

嗯,这里的 T' 就是线性部分用L'替换。一共32个轮密钥,生成过程天然就是流水线友好的——你只需要保留最近4个K值不断滚动。

硬件实现要点:面积与速度的取舍

  • S盒实现:128字节ROM,带1周期读延迟;如果追求高频,拆成4个BRAM并行读。
  • 线性L:纯组合逻辑,无时序风险。
  • 密钥扩展:可以和加密共享同一个S盒,但L'要单独实现。
  • 抗侧信道:SM4对功耗/电磁攻击较敏感。我曾在一次安全审计中发现,不加掩码的S盒查表直接导致密钥泄露。建议至少做一阶布尔掩码。
SM4 vs AES(硬件视角)
维度SM4AES-128
轮数3210
S盒规模8×8 共256字节8×8 共256字节
解密兼容直接复用,密钥逆序需独立逆S盒/逆列混合
典型面积(28nm)~8K GE~10K GE

看到表格了吧?SM4面积占优,但32轮会拉长关键路径。解决方案是每轮做1时钟周期,32个周期完成分组。如果要求高吞吐,可以展开8轮流水,代价是面积翻倍。

SM4加密流程(32轮Feistel结构) 128位明文P X₀ X₁ X₂ X₃ 轮函数 F (S盒 + L) ⊕ 异或、T变换、更新X₀ rkᵢ 32轮循环 i=1..32 128位密文C 解密:使用相同轮函数,轮密钥顺序取反(rk₃₁ → rk₀) 硬件可复用加密数据通路,只需翻转密钥ROM地址

图里画了整个数据流转:明文拆字后丢进32轮循环,每轮用轮函数和当前轮密钥更新X₀,然后四元组依次滚动。解密就是滚反向——从密文开始,用倒序的轮密钥走32轮,明文就回来了。

设计心得:算法越简单,陷阱越隐蔽

SM4的数学结构太对称了,导致硬件里随便一个时序错位都看不出来。我建议:

  • 仿真时务必做“已知向量测试”,网上国密局发布的标准测试向量直接灌进去对。
  • FPGA验证时加个计数器跑100万个随机分组,和软件参考结果逐字节比对。
  • 如果做HSM,别忘了把S盒ROM的读写保护加上——侧信道攻击最喜欢偷查表时泄露的汉明重量。