SEED 分组密码算法的轮函数设计与加密过程
题目描述:
SEED 是韩国开发的一种分组密码算法,采用 128 位分组长度和 128 位密钥长度。它基于 Feistel 网络结构,轮数为 16 轮。SEED 的核心在于其轮函数 F,该函数使用两个不同的非线性函数 G 和 H,并依赖于轮密钥 Ki 和轮常数 Ci。请详细讲解 SEED 算法中一轮的加密过程,特别是其轮函数 F 的设计、非线性函数 G 与 H 的具体计算步骤,以及轮密钥 Ki 与轮常数 Ci 如何参与运算。
讲解
我将为你循序渐进地讲解 SEED 算法的单轮加密过程和其轮函数 F 的设计细节。
第一步:SEED 的整体框架
首先,我们要理解 SEED 的整体结构。
-
基本参数:
- 分组长度:128 比特
- 密钥长度:128 比特
- 轮数:16 轮
- 结构:平衡 Feistel 网络
-
Feistel 网络回顾:
- 它将 128 比特的输入明文分成左右两部分,每部分 64 比特,记为 L₀ (左) 和 R₀ (右)。
- 对于 i = 0 到 15,执行以下运算:
- Lᵢ₊₁ = Rᵢ
- Rᵢ₊₁ = Lᵢ ⊕ F(Rᵢ, Kᵢ)
- 经过 16 轮后,输出 (L₁₆, R₁₆) 即为密文。
SEED 的精华就在于这个 轮函数 F。
第二步:轮函数 F 的输入与输出
轮函数 F 接受两个输入:
- 一个 64 比特的数据块(来自当前右半部分 Rᵢ)。
- 一个 64 比特的轮密钥 Kᵢ(从主密钥扩展而来)。
它产生一个 64 比特的输出,用于与左半部分 Lᵢ 进行异或。
F 函数的基本流程:
- 将 64 比特的输入 Rᵢ 与 64 比特的轮密钥 Kᵢ 进行加法模 2⁶⁴运算,得到一个 64 比特的中间值
T。
T = (Rᵢ + Kᵢ) mod 2⁶⁴ - 将这个 64 比特的
T分解成两个 32 比特的部分:T = T₁ || T₀,其中 T₁ 是高 32 比特,T₀ 是低 32 比特。 - 将 T₁ 和 T₀ 分别送入非线性函数 G 进行处理。
- 将 G 函数的两个输出结果,与一个轮常数 Cᵢ(也是 64 比特)进行加法模 2⁶⁴ 运算。
- 最后,将上一步得到的结果再次送入非线性函数 H,其输出就是最终的 64 比特结果。
流程可以用下图简单表示(我们会在后续步骤中填充细节):
Rᵢ -> (Rᵢ + Kᵢ) -> 拆分成T₁, T₀ -> G函数(T₁, T₀) -> (G_out + Cᵢ) -> H函数 -> F函数输出
第三步:非线性函数 G 的详细设计
函数 G 是 SEED 的核心混淆组件。它的输入是两个 32 比特的块(即 T₁ 和 T₀),输出也是两个 32 比特的块。
G 函数的核心是 4 个 8x8 的 S 盒(S0, S1, S2, S3)。这些 S 盒通过字节替换提供非线性。
G 函数的计算步骤:
-
输入拆分:G 的输入是两个 32 比特字
X₁和X₀(对应 T₁, T₀)。将它们分别拆分成 4 个字节。
X₁ = x₁₃ || x₁₂ || x₁₁ || x₁₀(每个 x 为 8 比特)
X₀ = x₀₃ || x₀₂ || x₀₁ || x₀₀ -
字节替换:
- 对
X₁的字节进行 S 盒替换,顺序是 S1, S0, S3, S2。 - 对
X₀的字节进行 S 盒替换,顺序是 S3, S2, S1, S0。 - 替换后得到新的 32 比特字
Y₁和Y₀。
- 对
-
混合操作:为了提供扩散,SEED 在 G 函数中引入了模加和异或运算。
Z₁ = (Y₁ + Y₀) mod 2³²Z₀ = (Y₁ + 2 * Y₀) mod 2³²(注意:这里的 2*Y₀ 是有限域 GF(2³²) 上的乘法吗?不,这里是模 2³² 整数乘法,即(2 * Y₀) mod 2³²,等价于 Y₀ 左移一位,如果溢出则丢弃高位并加 1 吗?实际上就是简单的整数相乘后取模)- 更常见的描述是:
Z₀ = (Y₁ + Y₀ + ((Y₁ * Y₀) mod 2³²)?不,SEED 标准文档中使用的是上面的线性组合。Z₀ = (Y₁ + (Y₀ << 1)) mod 2³²更直观,其中<< 1是左移一位,可能产生溢出。
实际上,SEED 规范中定义的公式为:
Z₁ = (Y₁ + Y₀) mod 2³²
Z₀ = (Y₁ + 2*Y₀ + (进位?))更准确地说,这里的2*Y₀是指(Y₀ * 2) mod 2³²。在计算机实现中,可以理解为:Z₀ = (Y₁ + (Y₀ << 1)) & 0xFFFFFFFF。如果 Y₀ 的最高位是 1,左移后相当于乘以 2 然后模 2³²。 -
输出:
(Z₁, Z₀)就是 G 函数的最终输出,共 64 比特。
第四步:轮常数 Cᵢ 的加法
得到 G 函数的 64 比特输出 Z = Z₁ || Z₀ 后,下一步是与轮常数 Cᵢ 进行加法。
- 轮常数 Cᵢ 是一个 64 比特的值,定义为:
Cᵢ = (KC₂ᵢ₋₂ || KC₂ᵢ₋₁)对于 i=1...16。
其中 KC 是固定的常数表。 - 计算:
W = (Z + Cᵢ) mod 2⁶⁴ - 将 W 分解为两个 32 比特字:
W = W₁ || W₀
第五步:非线性函数 H 的详细设计
函数 H 的结构与 G 函数完全相同。它的输入就是上一步得到的 W₁ 和 W₀。
H 函数的计算步骤(与 G 完全一样,但输入不同):
- 输入
W₁和W₀,拆分成字节。 - 对
W₁的字节应用 S 盒 (S1, S0, S3, S2)。 - 对
W₀的字节应用 S 盒 (S3, S2, S1, S0)。 - 得到中间值
U₁和U₀。 - 计算:
V₁ = (U₁ + U₀) mod 2³²V₀ = (U₁ + 2*U₀) mod 2³²
- 输出:
(V₁, V₀)就是 H 函数的最终输出,也就是整个轮函数 F 的 64 比特输出F(Rᵢ, Kᵢ) = V₁ || V₀。
第六步:一轮完整过程总结
现在,我们把所有步骤串联起来,描述 SEED 的第 i 轮加密:
- 输入:上一轮的左右两部分
Lᵢ₋₁和Rᵢ₋₁(初始为明文 L₀, R₀)。 - 轮函数 F 计算:
a.T = (Rᵢ₋₁ + Kᵢ) mod 2⁶⁴
b. 拆T为T₁(高32位),T₀(低32位)。
c. 调用 G(T₁, T₀) -> 得到(Z₁, Z₀)。
d.W = (Z₁||Z₀) + Cᵢ mod 2⁶⁴。
e. 拆W为W₁(高32位),W₀(低32位)。
f. 调用 H(W₁, W₀) -> 得到(V₁, V₀),这就是F_out。 - Feistel 更新:
Lᵢ = Rᵢ₋₁Rᵢ = Lᵢ₋₁ ⊕ F_out
- 输出:新的左右部分
(Lᵢ, Rᵢ),作为下一轮的输入。
第七步:S盒与常数的作用
- S盒 (S0, S1, S2, S3):它们是固定的 8 比特输入/8 比特输出的查找表,是算法非线性的主要来源。不同的 S 盒(顺序不同)在 G 和 H 函数中对高/低字节进行处理,增加了算法的复杂性。
- 轮常数 Cᵢ:它们的作用是打破自相似性。如果没有 Cᵢ,每一轮的 G 和 H 函数处理流程是完全相同的。加入依赖于轮数 i 的 Cᵢ,使得每一轮的中间状态都不同,增强了算法抵抗某些密码分析(如滑动攻击)的能力。
- 模 2³² 加法和线性组合:在 G/H 函数中,
Z₁ = Y₁+Y₀和Z₀ = Y₁+2*Y₀提供了快速的线性扩散,将 S 盒带来的非线性效应迅速扩散到更多的比特上。
总结
SEED 算法的轮函数 F 设计精巧,其特点是两级非线性处理:
- 第一级:
G函数处理(数据+轮密钥)。 - 第二级:
H函数处理(G函数输出+轮常数)。
这种 F = H( G( R+K ) + C ) 的结构,结合了密钥加、S盒非线性替换、线性模加扩散、轮常数扰动等多种操作,使得 SEED 在实现效率和安全性之间取得了良好的平衡。整个 16 轮的 Feistel 结构确保了输入的每一位都能充分混淆和扩散,从而构成了一个安全的 128 位分组密码。