3. SM4对称加密算法原理
SM4 是中国国家密码管理局发布的商用分组密码算法(原 SMS4),分组长度为 128 比特,密钥长度为 128 比特。SM4 采用非平衡 Feistel 结构,轮函数使用 32 轮迭代,加解密算法结构相同,仅轮密钥使用顺序相反。本章深入剖析 SM4 的 Feistel 结构、S 盒设计、密钥扩展算法、加密/解密流程及轮函数细节。
3.1 Feistel 结构与 SM4 的变体
SM4 采用 非平衡 Feistel 结构(Unbalanced Feistel Network),与经典 Feistel 结构(如 DES)的区别在于:每轮只处理 32 比特(1 个字),而非半个分组。这种设计使得 SM4 在硬件实现上更紧凑,轮函数更简洁。
3.2 S 盒设计
SM4 的 S 盒是 8×8 的置换表,输入 8 比特,输出 8 比特。S 盒的设计基于 仿射变换 + 逆映射 的代数结构,具有以下特性:
- 非线性度: 达到 112,抵抗线性密码分析。
- 差分均匀性: 为 4,抵抗差分密码分析。
- 代数次数: 为 7,抵抗高阶积分攻击。
- 无不动点: S(a) ≠ a,∀a。
| 行\列 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | D6 | 90 | E9 | FE | CC | E1 | 3D | B7 | 16 | B6 | 14 | C2 | 28 | FB | 2C | 05 |
| 1 | 2B | 67 | 9A | 76 | 2A | BE | 04 | C3 | AA | 44 | 13 | 26 | 49 | 86 | 06 | 99 |
| 2 | 9C | 42 | 50 | F4 | 91 | EF | 98 | 7A | 33 | 54 | 0B | 43 | ED | CF | AC | 62 |
| 3 | E4 | B3 | 1C | A9 | C9 | 08 | E8 | 95 | 80 | DF | 94 | FA | 75 | 8F | 3F | A6 |
| 4 | 47 | 07 | A7 | FC | F3 | 73 | 17 | BA | 83 | 59 | 3C | 19 | E6 | 85 | 4F | A8 |
| 5 | 68 | 6B | 81 | B2 | 71 | 64 | DA | 8B | F8 | EB | 0F | 4B | 70 | 56 | 9D | 35 |
| 6 | 1E | 24 | 0E | 5E | 63 | 58 | D1 | A2 | 25 | 22 | 7C | 3B | 01 | 21 | 78 | 87 |
| 7 | D4 | 00 | 46 | 57 | 9F | D3 | 27 | 52 | 4C | 36 | 02 | E7 | A0 | C4 | C8 | 9E |
| 8 | EA | BF | 8A | D2 | 40 | C7 | 38 | B5 | A3 | F7 | F2 | CE | F9 | 61 | 15 | A1 |
| 9 | E0 | AE | 5D | A4 | 9B | 34 | 1A | 55 | AD | 93 | 32 | 30 | F5 | 8C | B1 | E3 |
| A | 1D | F6 | E2 | 2E | 82 | 66 | CA | 60 | C0 | 29 | 23 | AB | 0D | 53 | 4E | 6F |
| B | D5 | DB | 37 | 45 | DE | FD | 8E | 2F | 03 | FF | 6A | 72 | 6D | 6C | 5B | 51 |
| C | 8D | 1B | AF | 92 | BB | DD | BC | 7F | 11 | D9 | 5C | 41 | 1F | 10 | 5A | D8 |
| D | 0A | C1 | 31 | 88 | A5 | CD | 7B | BD | 2D | 74 | D0 | 12 | B8 | E5 | B4 | B0 |
| E | 89 | 69 | 97 | 4A | 0C | 96 | 77 | 7E | 65 | B9 | F1 | 09 | C5 | 6E | C6 | 84 |
| F | 18 | F0 | 7D | EC | 3A | DC | 4D | 20 | 79 | EE | 5F | 3E | D7 | CB | 39 | 48 |
3.3 密钥扩展算法
SM4 的密钥扩展算法将 128 比特(4 个字)的初始密钥扩展为 32 个 32 比特轮密钥(rk0 ~ rk31)。扩展过程使用与加密轮函数相同的非线性变换,但使用不同的系统参数和固定参数。
3.3.1 密钥扩展步骤
- 初始化: 将 128 比特密钥分为 4 个字 (MK0, MK1, MK2, MK3)。
- 异或系统参数: Ki = MKi ⊕ FKi,其中 FK 为系统参数(固定值)。
- 迭代生成: 对于 i = 0..31,计算 rki = Ki+4 = Ki ⊕ T'(Ki+1 ⊕ Ki+2 ⊕ Ki+3 ⊕ CKi)。
其中 T' 变换与加密轮函数中的 T 变换类似,但线性变换 L' 不同:L'(B) = B ⊕ (B <<< 13) ⊕ (B <<< 23)。
| 参数 | 值(十六进制) | 说明 |
|---|---|---|
| FK0 | A3B1BAC6 | 系统参数,用于密钥初始化 |
| FK1 | 56AA3350 | |
| FK2 | 677D9197 | |
| FK3 | B27022DC | |
| CK0 | 00070E15 | 固定参数,每轮不同,共 32 个 |
| CK1 | 1C232A31 | |
| CK2 | 383F464D | |
| CK3 | 545B6269 |
3.4 轮函数详解
SM4 的轮函数 F 是算法的核心,输入为 4 个字 (Xi, Xi+1, Xi+2, Xi+3) 和轮密钥 rki,输出一个 32 比特字。轮函数由三部分组成:异或合成、S 盒非线性变换、线性变换 L。
3.4.1 轮函数公式
F(X0, X1, X2, X3, rk) = X0 ⊕ T(X1 ⊕ X2 ⊕ X3 ⊕ rk)
其中 T 变换 = L(τ(B))
τ(B) = (Sbox(B[0]), Sbox(B[1]), Sbox(B[2]), Sbox(B[3]))
L(B) = B ⊕ (B <<< 2) ⊕ (B <<< 10) ⊕ (B <<< 18) ⊕ (B <<< 24)
3.4.2 轮函数内部流程
3.5 加密/解密流程
3.5.1 加密流程
- 初始变换: 将 128 比特明文分为 4 个字 (X0, X1, X2, X3)。
- 32 轮迭代: 对于 i = 0..31,计算 Xi+4 = F(Xi, Xi+1, Xi+2, Xi+3, rki)。
- 反序变换: 输出密文 (Y0, Y1, Y2, Y3) = (X35, X34, X33, X32)。
3.5.2 解密流程
SM4 的解密与加密使用完全相同的算法结构,仅轮密钥使用顺序相反:加密使用 rk0, rk1, ..., rk31,解密使用 rk31, rk30, ..., rk0。这种设计使得加解密可以共用同一套硬件电路。
3.5.3 加解密流程对比
| 步骤 | 加密 | 解密 |
|---|---|---|
| 输入 | 128 比特明文 (X0~X3) | 128 比特密文 (Y0~Y3) |
| 轮密钥顺序 | rk0, rk1, ..., rk31 | rk31, rk30, ..., rk0 |
| 轮函数 | F(Xi, Xi+1, Xi+2, Xi+3, rki) | F(Xi, Xi+1, Xi+2, Xi+3, rk31-i) |
| 轮数 | 32 轮 | 32 轮 |
| 反序变换 | (X35, X34, X33, X32) | (X35, X34, X33, X32) |
| 输出 | 128 比特密文 | 128 比特明文 |
3.6 代码实现示例
以下为 SM4 轮函数和加密核心的 Python 实现片段:
# SM4 S 盒(256 字节)
SBOX = [
0xD6, 0x90, 0xE9, 0xFE, 0xCC, 0xE1, 0x3D, 0xB7,
0x16, 0xB6, 0x14, 0xC2, 0x28, 0xFB, 0x2C, 0x05,
# ... 完整 S 盒见上表
0x39, 0x48
]
def sm4_round_func(x0, x1, x2, x3, rk):
"""SM4 轮函数 F"""
# 异或合成
b = x1 ^ x2 ^ x3 ^ rk
# S 盒非线性变换 τ
t = 0
for i in range(4):
byte = (b >> (24 - i * 8)) & 0xFF
t |= SBOX[byte] << (24 - i * 8)
# 线性变换 L
t ^= ((t << 2) | (t >> 30)) & 0xFFFFFFFF
t ^= ((t << 10) | (t >>