SM4分组密码算法的轮常数生成过程
我将为你详细讲解SM4分组密码算法中轮常数的生成过程。这是一个非常重要但常被忽视的细节,它关系到算法的密钥扩展和轮函数运算。
题目描述
SM4是中国国家标准的分组密码算法,分组长度为128比特,密钥长度为128比特,采用32轮非平衡Feistel结构。在SM4的密钥扩展算法中,需要生成32个32比特的轮密钥(rk₀到rk₃₁)。而生成这些轮密钥时,需要使用一个预先定义的常数序列,称为“轮常数”或“固定参数”(Fixed Parameters)。这些常数是如何生成的?其设计原理和具体计算过程是什么?
解题过程(循序渐进讲解)
步骤1:理解轮常数的作用
在SM4的密钥扩展算法中,轮常数(记为CK,Cipher Key constants)用于与中间密钥数据结合,增加密钥扩展的非线性性和随机性,防止密钥扩展过程过于简单而被攻击。它们也确保了即使初始密钥全为0,扩展出的轮密钥也不会全为0,提高了算法对弱密钥的抵抗能力。
步骤2:确认轮常数的格式与数量
SM4需要生成32个轮密钥,因此相应地需要32个轮常数。每个轮常数是一个32比特(4字节)的无符号整数。所以,总共有32个常数,记为:
CK₀, CK₁, CK₂, ..., CK₃₁
每个CKᵢ ∈ {0, 1, ..., 2³²−1}。
步骤3:轮常数的生成公式(核心)
轮常数并不是随机选取的,而是由一个简单的线性公式生成,确保其可复现性和结构性。其生成方法如下:
设每个轮常数CKᵢ由4个字节构成:CKᵢ = (ckᵢ,₀, ckᵢ,₁, ckᵢ,₂, ckᵢ,₃),其中每个ckᵢ,ⱼ是一个字节(8比特)。
那么,每个字节的值为:
\[ ck_{i,j} = (4i + j) \times 7 \ (\text{mod} \ 256) \]
其中:
- i = 0, 1, 2, ..., 31 (对应32个轮常数)
- j = 0, 1, 2, 3 (对应每个常数的4个字节)
- 所有运算在整数域进行,结果取模256(因为一个字节的范围是0~255)。
- “×”表示乘法。
- 公式中的“7”是设计者选择的乘数,是一个小奇数,确保字节值充分变化。
步骤4:逐步计算示例(以CK₀和CK₁为例)
我们来手动计算前两个轮常数,验证公式。
计算CK₀ (i=0):
- j=0: ck₀,₀ = (4×0 + 0) × 7 = 0 × 7 = 0 (mod 256) = 0x00
- j=1: ck₀,₁ = (4×0 + 1) × 7 = 1 × 7 = 7 = 0x07
- j=2: ck₀,₂ = (4×0 + 2) × 7 = 2 × 7 = 14 = 0x0E
- j=3: ck₀,₃ = (4×0 + 3) × 7 = 3 × 7 = 21 = 0x15
所以,CK₀ = (0x00, 0x07, 0x0E, 0x15)。按照大端序(高位字节在前)组合成一个32位数:0x00070E15。
计算CK₁ (i=1):
- j=0: ck₁,₀ = (4×1 + 0) × 7 = 4 × 7 = 28 = 0x1C
- j=1: ck₁,₁ = (4×1 + 1) × 7 = 5 × 7 = 35 = 0x23
- j=2: ck₁,₂ = (4×1 + 2) × 7 = 6 × 7 = 42 = 0x2A
- j=3: ck₁,₃ = (4×1 + 3) × 7 = 7 × 7 = 49 = 0x31
所以,CK₁ = (0x1C, 0x23, 0x2A, 0x31),即0x1C232A31。
步骤5:完整常数表与验证
按照上述公式,我们可以计算出完整的32个轮常数。在实际的SM4标准文档或实现中,这些常数通常以预计算的常量数组形式给出,避免运行时重复计算。例如,前8个常数为(以十六进制表示):
CK₀ = 0x00070E15
CK₁ = 0x1C232A31
CK₂ = 0x383F464D
CK₃ = 0x545B6269
CK₄ = 0x70777E85
CK₅ = 0x8C939AA1
CK₆ = 0xA8AFB6BD
CK₇ = 0xC4CBD2D9
...
你可以观察到,每增加一个i(即每生成下一个常数),每个字节值大致增加28(因为4×7=28),但由于模256运算,会呈现周期性的线性序列。
步骤6:与密钥扩展算法的结合
在SM4密钥扩展算法中,轮常数CKᵢ被用在轮密钥生成的“T’变换”中。具体来说(简述过程):
- 初始128比特密钥MK被分为4个32比特字:MK = (MK₀, MK₁, MK₂, MK₃)。
- 计算中间值Kᵢ = MKᵢ ⊕ FKᵢ(i=0,1,2,3),其中FK是系统固定参数(与CK不同)。
- 然后通过32轮迭代生成轮密钥rkᵢ:
\[ rk_i = K_{i+4} = K_i \oplus T’(K_{i+1} \oplus K_{i+2} \oplus K_{i+3} \oplus CK_i) \]
其中T’是一个由非线性变换τ和线性变换L’构成的伪随机变换。
4. 这里的CKᵢ就是第i轮的轮常数,它与中间密钥材料进行异或,作为T’变换的输入一部分。
步骤7:设计原理分析
为什么这样设计轮常数?
- 简单性与确定性:线性公式(4i+j)×7极其简单,易于在标准中描述和实现,且在任何平台上计算结果完全一致,保证了算法的可移植性和确定性。
- 无重复性:由于公式是线性的且模256,在i=0到31范围内,生成的32×4=128个字节值各不相同(你可以验证不会重复),这为每轮密钥扩展提供了不同的“扰动”因子。
- 足够的随机性(伪随机):虽然公式简单,但结合后续的T’变换(包含非线性S盒和线性变换),足以打乱输入,生成看起来随机的轮密钥。常数本身不需要密码学强度的随机性。
- 避免弱密钥:即使初始密钥MK全为0,由于FK和CK的存在,扩展出的轮密钥也不会全为0,避免了明显的弱密钥。
- 效率:预计算或直接计算都非常快,几乎不消耗资源。
总结
SM4的轮常数生成是一个优雅而实用的设计。它通过一个极其简单的线性公式CKᵢ = (4i+j)×7 mod 256,为32轮密钥扩展提供了可复现、无重复的扰动参数。这些常数本身不提供密码学强度,但与密钥扩展算法中的T’变换结合后,能有效协助生成安全、随机的轮密钥。理解这个过程有助于我们深入把握SM4密钥扩展算法的整体结构和设计者的匠心。