第2章 SM4加密与解密流程:基本轮函数、非线性变换
SM4的加解密流程,说白了就是一套「轮函数反复套圈」的过程。我刚接触这个算法时,总觉得32轮迭代很吓人,但拆开看,每一轮干的活其实就三件事:加轮密钥、非线性变换、线性变换。你吃透一轮,后面31轮全是重复。
2.1 整体加解密框架
SM4采用128位分组、128位密钥,32轮迭代。加密和解密共用同一个轮函数,唯一区别是轮密钥的使用顺序——解密时把加密的轮密钥倒过来用。为什么能这样?因为轮函数是可逆的,而且逆变换刚好是它自己换了个顺序。
2.2 轮函数结构
先看加密轮函数。设第i轮的输入为 \((X_i, X_{i+1}, X_{i+2}, X_{i+3})\),轮密钥为 \(rk_i\),输出为 \(X_{i+4}\)。我习惯把它写成下面这样:
X_{i+4} = X_i ^ L( τ( X_{i+1} ^ X_{i+2} ^ X_{i+3} ^ rk_i ) )
看不懂公式没关系,咱们分步拆解:
- 轮密钥加:把后三个字与轮密钥异或:\(A = X_{i+1} \oplus X_{i+2} \oplus X_{i+3} \oplus rk_i\)
- 非线性变换 τ:把32位的A拆成4个字节,分别查S盒,再合并成32位字
- 线性变换 L:把τ的输出做循环移位异或(类似扩散)
- 结果异或:L的输出与 \(X_i\) 异或,得到 \(X_{i+4}\)
每一轮就这四步,你想想看,是不是很简单?
2.3 核心秘籍:非线性变换 τ
τ就是把32位分成4个字节,每个字节丢进S盒,得到4个新字节再拼回32位。我个人喜欢用查表法实现——预计算好256个字节的S盒表,每轮查4次。SM4的S盒是固定写死的,网上能查到16×16的矩阵。这里我贴出关键部分的伪代码:
unsigned int tau(unsigned int A) {
unsigned char b0 = (A >> 24) & 0xFF;
unsigned char b1 = (A >> 16) & 0xFF;
unsigned char b2 = (A >> 8) & 0xFF;
unsigned char b3 = A & 0xFF;
return (Sbox[b0] << 24) | (Sbox[b1] << 16) |
(Sbox[b2] << 8) | Sbox[b3];
}
2.4 线性变换 L 与解密逆变换
线性变换L负责扩散。加密时它定义为:
L(B) = B ^ (B<<<2) ^ (B<<<10) ^ (B<<<18) ^ (B<<<24)
其中 <<< 表示循环左移。这个变换保证了「雪崩效应」——输入改1bit,输出会变很多bit。解密时用的不是L,而是它的逆变换L_inv:
L_inv(B) = B ^ (B<<<2) ^ (B<<<4) ^ (B<<<8) ^ (B<<<12) ^
(B<<<14) ^ (B<<<16) ^ (B<<<18) ^ (B<<<22) ^ (B<<<24) ^ (B<<<30)
注意看,L_inv比L多了好几个异或项。你想想看,这其实是数学上的可逆性保证,实际实现时如果偷懒直接用L去解密,输出会彻底乱掉。我遇到过有人把L_inv写错,调试了整整两天。
| 环节 | 加密 | 解密 |
|---|---|---|
| 轮密钥顺序 | rk[0], rk[1], ..., rk[31] | rk[31], rk[30], ..., rk[0] |
| 非线性变换 τ | 完全相同 | 完全相同 |
| 线性变换 | L (循环左移异或) | L_inv (更多循环异或) |
| 输出生成 | Xi ^ L(τ(...)) | Xi ^ L_inv(τ(...)) |
2.5 解密流程的对称性
SM4解密其实就是把加密的轮函数「倒过来走」。你看上面表格,轮密钥逆序、线性变换取逆,其他一概不变。所以你在写性能测试程序时,可以复用同一套轮函数代码,通过一个「加密/解密」标志来切换L还是L_inv。我个人习惯在轮函数参数里传一个函数指针,指向L或L_inv,这样32轮迭代的循环体不需要if判断,性能更高。
🔑 核心结论:SM4的32轮迭代中,每一轮都在做同一套「异或 → 查表 → 移位异或」。解密只是旋转了密钥方向、换了线性变换的系数。理解轮函数,你就抓住了SM4的七寸。
2.6 性能审计关注点
做并发数性能审计时,轮函数的几个瓶颈点需要格外留意:
- S盒查表粒度:每轮4次查表,32轮就是128次查表。如果共享S盒表有缓存竞争,建议每个线程预分配自己的S盒副本(只读,无锁)。
- 循环移位的SIMD化:L变换中的循环左移在x86上可以用_mm_rol_epi32加速,能省不少指令。
- 轮密钥预计算:所有32轮轮密钥可以一次性提前算好,加解密时直接读取数组。我见过有人每轮都重新算密钥,吞吐量直接掉一半。
好了,这一章的核心内容就是这些。理解轮函数的结构,后面讲并发性能测试时,你才能知道哪些地方值得优化。记住:轮函数是SM4的灵魂,查表和移位是性能的左右手。