SM4算法原理:分组长度、密钥长度、轮函数与S盒设计
好,我们进入正题。SM4算法在国密体系里算是个“老熟人”了。说实话,第一次看它的时候,我心里想:“这不就有点像AES吗?”但真正上手做项目后才发现,它的设计理念和实现细节都有自己的讲究。别急,我们从最基础的参数开始聊。
基础参数:分组长度与密钥长度
SM4的分组长度是128位,密钥长度也是128位。嗯,和AES-128完全一样。但是你别以为它就是个“山寨AES”。它的轮函数结构、S盒生成逻辑和密钥扩展算法都和AES不一样。我当年刚开始做SM4实现时,因为没注意这个差异,直接套了AES的流水线,结果跑出来数据全是错的——后来排查了一整天,才发现是S盒查表地址算错了。
- 分组长度:128 bits(16字节)
- 密钥长度:128 bits(16字节)
- 轮数:32轮
- 轮密钥长度:每轮32 bits(4字节)
你想想看,为什么是128位?这是平衡安全性和性能的结果。过短不安全(比如DES的56位早就被破),过长则不必要的增加了硬件面积。SM4选择128位,既能抵抗暴力破解,又适合在嵌入式设备和智能卡上跑。
轮函数结构:合成置换T
SM4的轮函数核心就一个字——T。这个T不是简单的查表,而是由两部分组成:非线性变换τ和线性变换L。我习惯叫它“合成置换”。
每一轮的具体操作是(假设当前轮输入为 (Xi, Xi+1, Xi+2, Xi+3),轮密钥为 rki):
- 计算
B = Xi+1 ⊕ Xi+2 ⊕ Xi+3 ⊕ rki,得到32位中间结果。 - 对B进行非线性变换τ:将B拆成4个8位字节,分别送入4个并行的S盒,输出4个8位结果,拼成32位。
- 对τ的输出进行线性变换L:一个32位循环移位异或操作。
- 最后
Xi+4 = Xi ⊕ T(B),即本轮结果。
说白了,每一轮只更新一个字节块(最左边的那个32位),其他三个直接向右平移。这和AES每轮更新整个状态矩阵是完全不同的策略。我曾在一个FPGA项目里对比过这两种结构,SM4的移位寄存器更容易实现高吞吐。
S盒设计
S盒是SM4的非线性核心,也是整个算法唯一引入高度非线性的地方。SM4的S盒是8输入8输出的置换。它的构造方法很有意思:
- 首先在GF(28)上取逆乘法(和AES一样,用欧拉定理快速计算)。
- 然后对结果进行仿射变换:一个8×8的二元矩阵乘法加上一个常数向量。
- 注意,SM4的仿射矩阵和AES不同——这是它们最大的区别。
我当年看过SM4的官方文档,发现它的S盒生成过程里有个“固定子字节”的预处理。为什么要这么做?为了消除代数结构,防止某些代数攻击。说实话,这设计我在做分析时才真正理解——它把原来在GF(2)上的简单关系打散了。
下面给一个S盒的部分查找表(只截取前8个值做个示意):
| 输入(十六进制) | 0x00 | 0x01 | 0x02 | 0x03 | 0x04 | 0x05 | 0x06 | 0x07 |
|---|---|---|---|---|---|---|---|---|
| 输出(十六进制) | 0xD6 | 0x90 | 0xE9 | 0xFE | 0xCC | 0xE1 | 0x3D | 0xB7 |
int8_t 存储。
密钥扩展算法
SM4的密钥扩展和加密轮函数几乎一模一样,只是把线性变换L换成了一个更简单的版本(叫 L')。初始密钥16字节分成4个32位字 (MK0, MK1, MK2, MK3),然后通过固定参数 FK 和 CK 生成32个轮密钥。
// 伪代码(仅示意)
uint32_t K[4];
K[0] = MK[0] ^ FK[0];
K[1] = MK[1] ^ FK[1];
K[2] = MK[2] ^ FK[2];
K[3] = MK[3] ^ FK[3];
for (int i = 0; i < 32; i++) {
uint32_t B = K[i+1] ^ K[i+2] ^ K[i+3] ^ CK[i];
uint32_t S = T'(B); // T' 使用 L' 代替常规L
rk[i] = K[i] ^ S;
K[i+4] = rk[i];
}
注意这里的 FK 是固定值(系统参数),CK 是中间常数(由循环移位生成)。这样的设计使得密钥扩展和加密用同一套硬件逻辑,节省面积。我记得有个客户问过:既然密钥扩展和加密结构一样,那能不能用同一组S盒?(可以,但要注意轮数不同,加密32轮,扩展也是32轮,正好复用)。
整体结构一张图
我把SM4的轮函数内部结构画成了下面这张SVG流程图。你看完应该能秒懂数据是怎么流动的。
与AES的简单对比
下面这个表格帮你快速对比SM4和AES的关键差异。做算法选型时你会用得上。
| 项目 | SM4 | AES |
|---|---|---|
| 分组/密钥长度 | 128/128 | 128/128/192/256 |
| 轮数 | 32轮 | 10/12/14轮 |
| 轮函数结构 | Feistel-like(每轮更新1/4块) | SPN(每轮更新整块) |
| S盒构造 | GF(2^8)求逆+仿射(不同矩阵) | GF(2^8)求逆+仿射 |
| 密钥扩展 | 与加密共用S盒和轮函数 | 独立扩展(Rijndael密钥调度) |
| 硬件实现难度 | 低(复用率高) | 中(每轮需要S盒+列混合) |
好了,SM4的核心原理就这么多。分组长度128位、密钥长度128位、32轮、合成置换T、以及那个基于有限域求逆的S盒——这些就是你实现SM4时的所有“武器”。下一张图我建议你自己画一遍轮函数的数据流,画完之后很多细节就刻在脑子里了。