CAST-128分组密码算法
CAST-128是一种对称分组密码算法,采用传统的Feistel网络结构。它的分组长度为64位,密钥长度可变(从40位到128位,以8位递增)。算法由Carlisle Adams和Stafford Tavares设计,以其设计者的首字母命名。CAST-128以其简单、高效和可证明的安全性特性而闻名。算法的核心在于其复杂的轮函数,该函数结合了非线性的S盒、依赖于密钥的旋转和异或操作。今天,我们将详细讲解CAST-128算法的加密过程,重点关注其轮函数的设计。
1. 算法总体结构
CAST-128是一个平衡的Feistel网络密码。对于64位的明文分组,它将其分为两个32位的半块:左半部分 \(L_0\) 和右半部分 \(R_0\)。
算法共有16轮(对于长度大于等于80位的密钥)或12轮(对于长度小于80位的密钥)。我们今天以最常见的16轮版本为例。
每一轮的基本操作如下(i 从1到16):
- \(R_i = L_{i-1} \oplus F(R_{i-1}, K_i)\) ,其中F是轮函数,\(K_i\) 是第i轮的轮密钥。
- \(L_i = R_{i-1}\)。
在16轮之后,最后会不交换左右两半,即最终的左右半块是 \((L_{16}, R_{16})\),将它们拼接成64位数据即为密文。
2. 轮函数F的设计
轮函数F是CAST-128的核心。它接收一个32位的输入(来自上一轮的右半块)和一个32位的轮密钥 \(K_i\),输出一个32位的结果。F函数内部又分为三个“类型”的操作,分别称为Type 1, Type 2, Type 3。在16轮中,奇数轮(1,3,5…)和偶数轮(2,4,6…)分别使用Type 1和Type 2,而中间的第3,7,11,15轮(如果你数一下,是第3,7,11,15轮)实际上也使用了Type 1,但算法设计中,第5,9,13轮被指定为Type 3。不过,为了让讲解更清晰,我们聚焦于最核心的Type 1 和 Type 2 结构。它们的区别主要在于轮密钥如何与中间数据结合。
我们先看Type 1轮函数的步骤:
- 密钥加法: 将32位的输入数据与32位的轮密钥 \(K_i\) 进行加法运算(模 \(2^{32}\)),得到一个32位的结果I。这个“加法”是模 \(2^{32}\) 的整数加法。
- S盒替换: 将上一步得到的32位结果I,从高位到低位(从左到右)分成四个8位字节。我们记作:\(I = [Ia, Ib, Ic, Id]\),其中Ia是最高有效字节。
然后,用四个不同的8x32位S盒(即输入8位,输出32位)分别对每个字节进行替换:- \(S_1\) 处理字节 Ia,得到32位输出 \(S1[Ia]\)。
- \(S_2\) 处理字节 Ib,得到32位输出 \(S2[Ib]\)。
- \(S_3\) 处理字节 Ic,得到32位输出 \(S3[Ic]\)。
- \(S_4\) 处理字节 Id,得到32位输出 \(S4[Id]\)。
- 组合与非线性混合: 将上述四个32位输出进行组合。具体操作是:
- \(S1[Ia]\) 和 \(S2[Ib]\) 进行按位异或(XOR),得到中间值A。
- \(S3[Ic]\) 和 \(S4[Id]\) 进行按位异或(XOR),得到中间值B。
- 然后,从B中减去A(模 \(2^{32}\)),得到中间值C。
- 最后,从\(S1[Ia]\) 中减去 \(S3[Ic]\)(模 \(2^{32}\)),然后与中间值C进行按位异或,得到最终结果D。
- 循环移位: 对结果D进行循环左移,移位的位数由轮密钥 \(K_i\) 的最低有效5位(即 \(K_i \mod 32\))决定。这个移位操作是循环左移,即移出的位从右侧补入。移位后的32位数据就是本轮F函数的最终输出。
Type 2轮函数的步骤与Type 1非常相似,但有两个关键区别:
- 密钥操作顺序不同: 在Type 2中,首先将输入数据与轮密钥 \(K_i\) 进行按位异或(XOR),而不是加法。
- S盒处理顺序不同: 在S盒替换步骤,Type 2使用的顺序是:用 \(S_1\) 处理字节Id, \(S_2\) 处理字节Ic, \(S_3\) 处理字节Ib, \(S_4\) 处理字节Ia。即字节处理顺序是反向的。
其余的“组合与非线性混合”和“循环移位”步骤与Type 1完全相同。
3. 轮密钥生成(简要概述)
为了完整性,我们简述轮密钥如何生成。CAST-128有一个复杂的密钥扩展算法,它将用户密钥(40-128位)扩展为16个32位的轮密钥 \(K_1\) 到 \(K_{16}\)。
- 密钥填充: 将用户密钥填充或截断为128位。如果不足128位,高位用0填充。
- 分割: 将128位密钥分成四个32位的子密钥:\(K_a, K_b, K_c, K_d\)。
- 密钥表生成: 利用一个预定义的、复杂的密钥扩展函数(涉及模加、循环移位、以及使用与主算法不同的另一组S盒),以上述四个子密钥为基础,生成一系列中间值,最终导出16个轮密钥。这个过程的细节非常繁琐,但其核心思想是确保轮密钥之间具有高度的非线性和独立性,并且密钥的每一位都影响多个轮密钥。
4. 总结加密流程
现在,让我们串联起完整的加密过程:
- 输入: 64位明文P,用户密钥K。
- 初始化:
- 执行密钥扩展,生成16个32位轮密钥 \(K_1\) 到 \(K_{16}\)。
- 将明文P分为 \(L_0\)(高32位)和 \(R_0\)(低32位)。
- 16轮迭代(i从1到16):
- 根据轮数i决定使用Type 1(奇数轮)还是Type 2(偶数轮)的F函数,计算 \(F(R_{i-1}, K_i)\)。
- \(R_i = L_{i-1} \oplus F(R_{i-1}, K_i)\)。
- \(L_i = R_{i-1}\)。
- 最终输出: 完成16轮后,输出为 \((L_{16}, R_{16})\)。注意这里没有像标准Feistel网络那样在末尾进行一次交换。将 \(L_{16}\) 和 \(R_{16}\) 直接拼接成64位,即为密文C。
5. 解密过程
解密过程与加密过程完全对称,因为Feistel网络具有自反性。唯一的不同是轮密钥的使用顺序是反的。
- 将64位密文C分为 \(R_{16}\) 和 \(L_{16}\)。
- 对于i从16下降到1:
- \(R_{i-1} = L_i\)。
- \(L_{i-1} = R_i \oplus F(R_{i-1}, K_i)\)。 (注意,这里的 \(R_{i-1}\) 就是上一步刚得到的值,而F函数使用的是与加密时相同的第i轮密钥 \(K_i\))。
- 最终得到 \((L_0, R_0)\),拼接后即得原始明文。
通过上述步骤,你可以看到CAST-128如何通过其精心设计的、结合了模加/减、异或、大S盒和依赖于密钥的循环移位的轮函数,在Feistel框架下实现了高度的非线性和扩散,从而保证了其安全性。其密钥扩展过程也确保了即使较短的密钥也能被充分混合。