文章目录
- Traditional Block Cipher Structure
- Stream Ciphers and Block Ciphers
- Feistel Cipher
- References
Traditional Block Cipher Structure
Stream Ciphers and Block Ciphers
序列密码也称为流密码 (Stream Ciphers), 它是指每次对数字数据流的一个比特或一个字节进行加密的一种密码。我们在《对称加密算法(一)(替换算法,Caesar, Playfair, Hill Cipher,Polyalphabetic Cipher)》中介绍的 autokeyed Vigenère cipher 以及 Vernam cipher 就属于序列密码。序列密码利用密钥产生一个密钥流 Z=Z1Z2Z3…,然后利用此密钥流依次对明文 X=X0X1X2… 进行加密。
如果采用 one-time pad 的思想,密钥流是随机的,且和明文长度相同,那么攻击者除了掌握密钥流,其他任何手段都无法进行破解。然而,密钥流必须事先通过一些独立和安全的渠道提供给两个用户,但这种完全随机且长度非常长的密钥流生成是非常困难的(logistical problems)。
因此,由于实际原因,比特流生成器是作为一个算法程序来实现的,以便加密比特流可以由两个用户产生,如下图所示:
比特流生成器是一个由密钥控制的算法,我们要求它必须产生一个较为强大的密钥流。也就是说,攻击者很难根据密钥流的已知部分来预测后面的部分。两个用户只需要共享生成密钥,且都可以生成密钥流。
分组密码(block cipher)将明文消息编码表示后的数字序列,划分成长度为 n 的组,每组分别在密钥的控制下变换成等长的输出数字序列。通常,分组大小会取 64 比特或者 128 比特。分组密码的框图如下所示:
一般来说,分组密码比序列密码的应用更加广泛。绝大多数基于网络的对称加密应用都使用了分组密码。
Feistel Cipher
目前使用的几种重要的对称分组加密算法都是基于一种被称为费斯妥密码(Feistel block cipher)的对称结构。
在分组密码中,我们要把 n 比特的明文块转换为 n 比特的密文块。为了保证加密是可逆(reversible,或者叫 nonsingular)的(可以解密),我们需要生成的密文块都是唯一的。对于 n 比特的分组,我们总共会有 2 n 2^n 2n 个可能的密文块(可逆加密)。下面列出了 n=2 时一组可逆和不可逆加密的示意:
下图说明了 n=4 时的一个分组密码工作示意。一个 4 比特的输入会产生 16个 可能的密文块中的一个,解密时密文块会被映射回对应的输入:
该过程的映射规则由一个映射表定义:
我们将这种分组密码称为理想分组密码,因为它允许从明文块中进行最大数量的加密映射。
但对于理想分组密码来说,如果 n 比较小,例如上例中的 4,那么它几乎等价于传统的替换密码,这意味着它可以通过对统计特性的分析而被破解。如果 n 足够大,并且允许在明文和密文之间进行任意的可逆替换,那么明文的统计特征就会被掩盖,这种类型的密码分析就不可行了。但实现这样一个足够大且任意可逆的理想分组密码显然是不切实际的。
且这种方法的另一个问题在于,映射规则本身就含有密钥。例如,对于上面的映射表,我们的密钥其实就是第二列的 ciphertext,长度为 (4 bits) × (16 行) = 64 bits.
基于这些问题,Feistel 指出,我们需要的是对一个较大 n 值的理想分组密码系统的近似,且可以用易实现的器件去构造。
Feistel 提出,我们可以利用乘积密码的概念来近似理想的分组密码,即依次执行两个或更多的加密算法,使最终的乘积结果比单一的加密算法结果都更具有鲁棒性。该方法的本质是使用一个密钥长度为 k、块长度为 n 的分组密码,允许 2 k 2^k 2k 个可能的转换,而不再是理想分组密码的 2 n 2^n 2n 个转换。
实际上,Feistel 的设计基于香农的一个提议,即设计一个交替使用混淆(confusion)和扩散(diffusion)的乘积密码。
在密码学中,混淆(confusion)和扩散(diffusion)是设计密码学算法的两种主要方法。这样的定义最早出现在克劳德·香农 1945 年的论文《密码学的数学理论》当中。
在克劳德·香农的定义之中,混淆是一种使密钥与密文之间的关系尽可能模糊的加密操作;而扩散则主要是用来使明文和密文的关系变得尽可能的复杂,明文中任何一点小更动都会使得密文有很大的差异。
实现混淆常用的一个方法就是替换(substitution)。而扩散是一种为了隐藏明文的统计特性而将一个明文符号的影响扩散到多个密文符号的加密操作。最简单的扩散就是位置换(permutation)。
维基百科
一个扩散的例子为
y
n
=
(
∑
i
=
1
k
m
n
+
k
)
m
o
d
26
y_n=\left(\sum_{i=1}^k m_{n+k}\right)\ {\rm mod}\ 26
yn=(i=1∑kmn+k) mod 26
即使用接下来的连续 k k k 个字符来得到一个密文字符。
下面我们给出费斯妥密码的结构:
该图左边为加密过程,而右边为解密过程。加密算法的输入为长度
2
w
2w
2w 比特的明文块以及密钥
K
K
K。明文块被等分为两部分,
L
E
0
LE_0
LE0 和
R
E
0
RE_0
RE0,在每一轮
i
=
1
,
2
,
…
,
n
i=1, 2,\dots,n
i=1,2,…,n,我们都计算
L
E
i
=
R
E
i
−
1
LE_i=RE_{i-1}
LEi=REi−1
R E i = L E i − 1 ⊕ F ( R E i − 1 , K i ) RE_i=LE_{i-1}\oplus {\rm F}(RE_{i-1}, K_i) REi=LEi−1⊕F(REi−1,Ki)
而在解密时,我们的方法是完全一样的,除了密钥的顺序是相反的:
L
D
i
=
R
D
i
−
1
LD_i=RD_{i-1}
LDi=RDi−1
R D i = L D i − 1 ⊕ F ( R D i − 1 , K n − i + 1 ) RD_i=LD_{i-1}\oplus {\rm F}(RD_{i-1}, K_{n-i+1}) RDi=LDi−1⊕F(RDi−1,Kn−i+1)
从图中我们还可以注意到,第 i i i 轮加密过后我们会得到 L E i ∣ ∣ R E i LE_i||RE_i LEi∣∣REi,而对应的 16 − i 16-i 16−i 轮次后的解密则有 L D 16 − i ∣ ∣ R D 16 − i = R E i ∣ ∣ L E i LD_{16-i}||RD_{16-i}=RE_i||LE_i LD16−i∣∣RD16−i=REi∣∣LEi. 下面我们来具体计算一下看以上结果是否正确。
最后一轮加密过后我们有
L
E
16
=
R
E
15
LE_{16}=RE_{15}
LE16=RE15
R
E
16
=
L
E
15
⊕
F
(
R
E
15
,
K
16
)
RE_{16}=LE_{15}\oplus {\rm F}(RE_{15}, K_{16})
RE16=LE15⊕F(RE15,K16)
两部分互换位置之后得到了最终的密文
R
E
16
∣
∣
L
E
16
RE_{16}||LE_{16}
RE16∣∣LE16,所以解密过程的输入即为
R
E
16
∣
∣
L
E
16
RE_{16}||LE_{16}
RE16∣∣LE16。
在解密的第一轮:
L
D
1
=
R
D
0
=
L
E
16
=
R
E
15
LD_1=RD_0=LE_{16}=RE_{15}
LD1=RD0=LE16=RE15
R D 1 = L D 0 ⊕ F ( R D 0 , K 16 ) = R E 16 ⊕ F ( R E 15 , K 16 ) = [ L E 15 ⊕ F ( R E 15 , K 16 ) ] ⊕ F ( R E 15 , K 16 ) = L E 15 \begin{aligned} RD_1=&LD_0\oplus {\rm F}(RD_0, K_{16}) \\ =& RE_{16} \oplus {\rm F}(RE_{15}, K_{16}) \\ =& [LE_{15}\oplus {\rm F}(RE_{15}, K_{16})] \oplus {\rm F}(RE_{15}, K_{16}) \\ =& LE_{15} \end{aligned} RD1====LD0⊕F(RD0,K16)RE16⊕F(RE15,K16)[LE15⊕F(RE15,K16)]⊕F(RE15,K16)LE15
最后一步计算用到了异或(XOR)的以下性质:
[
A
⊕
B
]
⊕
C
=
A
⊕
[
B
⊕
C
]
[A \oplus B] \oplus C=A\oplus[B \oplus C]
[A⊕B]⊕C=A⊕[B⊕C]
D ⊕ D = 0 D\oplus D=0 D⊕D=0
E ⊕ 0 = E E \oplus 0=E E⊕0=E
在上面的推导中我们应该也注意到了,函数 F F F 并没有参与到实际的运算当中,这意味着它不要求是一个可逆函数,即使它总是输出一个常数,我们也可以成功解密。
费斯妥结构的实现涉及到以下几个参数的选择:
- Block size:更大的块长度意味着更高的安全性(更强的扩散),但同时也会导致加密和解密的速度降低。
- Key size:和块大小类似,更大的密钥长度意味着更高的安全性(更强的混淆),但同时也会导致加密和解密的速度降低。
- Number of rounds:轮次越多,越能保证安全,通常会选择 16.
- Subkey generation algorithm:子密钥生成算法越复杂,密码分析就越困难。
- Round function F:当然也是越复杂安全性越好。
尽管看起来我们把设计搞得越复杂越好,但越复杂的东西也意味着越高的成本,我们还需考虑下面两个因素:
- Fast software encryption/decryption:在许多情况下,加密被嵌入到应用程序或实用功能中,因此,算法的执行速度需要被考虑。
- Ease of analysis:尽管我们希望使我们的算法尽可能难以进行密码分析,但使算法易于分析也有很大的好处。也就是说,如果算法能够被简明扼要地解释,那么就更容易分析该算法的漏洞,从而对其做出改进。
References
Cryptography and Network Security: Principles and Practice, 7th Edition, ISBN 978-0-13-444428-4, by William Stallings, published by Pearson Education.