2 - 流运算

流运算

我的博客
原书:《Understanding Cryptography: A Text book for Students and Practitioners》

现有的加密算法可以划分为:

  • 对称加密
    • 块加密
    • 流加密
  • 非对称加密
  • 协议

2.1 介绍

2.1.1 流运算与块运算对比

对称加密可以分为块加密与流加密。

流加密 对比特位单独加密,这通过将密钥流加到原文流上实现。流加密又可以分为同步流运算与异步流运算。同步流运算的密钥流只与密钥有关,而异步流加密则会与上一个加密后的密文有关。

块加密 使用同一个密钥一次加密一整块原文。这意味着对任何原文比特的加密会依赖于块中的其他原文比特。实际上, 大部分的块加密如 AES: Advanced Encryption Standard 具有 128比特位的块加密长度,或者 61 比特位的块加密长度,比如DES: Data Encryption Standard 以及 3DES 算法。

一些关于流加密与块加密的信息:

  1. 在互联网上块运算是主流
  2. 流运算轻量且快速
  3. 流运算消耗更少的资源

2.1.2 流运算的加密与解密

前面提到过,流运算加密会对单个比特进行加密。它通过将每一个原文比特位与密钥做加运算并模 2 得到密文:

定义2.1.1 流运算加密与解密过程中,原文流,密文流,以及密钥流由各自的单个比特位组成,其中 x 为原文,y 为密文,s 为密钥:

\[x_i,y_i,s_i \in \{0,1\} \]

加密过程:

\[y_i = e_{s_i}(x_i) \equiv x_i + s_i \quad mod \quad 2 \]

解密过程:

\[x_i = d{s_i}(y_i) \equiv y_i + s_i \quad mod \quad 2 \]

可以看到,加密过程与解密过程都是加法与模 2,两个函数是同一个。

为什么加密与解密过程使用相同的函数?

我们必须证明解密函数能够再次生成原文比特,我们直到密文比特是通过加密函数,我们可以将加密函数插入到解密函数中:

\[\begin{align} d_{s_i}(y_i) \equiv y_i + s_i \quad mod \quad 2 \\ \equiv (x_i + s_i) + s_i \quad mod \quad 2 \\ \equiv x_i + s_i + s_i \quad mod \quad 2 \\ \equiv x_i + 2s_i \quad mod \quad 2 \\ \equiv x_i + 0 \quad mod \quad 2 \\ \equiv x_i \quad mod \quad 2 \end{align} \]

证明的关键点在于:

\[2s_i \quad mod \quad 2 \equiv 0 \quad mod \quad 2 \\ s_i = 0,2s_i = 2\cdot 0 \equiv 0 \quad mod \quad 2 \\ s_i = 1,2s_i = 2\cdot 1 \equiv 2 \equiv 0\quad mod \quad 2 \]

为什么模数 2 加法是一个好的加密函数?

如果我们进行模 2 算术,可能得到的值只有 0 或 1。因此我们可以将模 2 运算转换成布尔函数,比如与、或、非运算。让我们看一下模 2 运算的加法:

x s x + y mod 2
0 0 0
0 1 1
1 0 1
1 1 0

可以看到,这是异或运算。异或运算在现代加密算法中扮演着重要的角色。

密钥流的本质是什么?

流运算加密的安全性完全依赖于密钥流。现在我们可以对密钥流做简单的讨论,对于攻击者而言,密钥流像是随机序列。否则攻击者可以猜出密钥流比特来完成解密。因为我们首先是获取随机数。

2.2 随机数以及不可打破的流运算

2.2.1 随机数生成

就像前面介绍的,流运算加密过程是十分简单的,我们只需要找到一个何理的密钥流就可以确保流加密的安全性。因此我们需要随机数。

真随机数生成器(TRNG: True Random Number Generators)

真随机数的生成基于物理过程,比如半导体噪声。在密码学中,在生成会话密钥时需要真随机数,这个会话密钥之后会在会话参与者之间交换。

伪随机数生成器(PRNG: Pseudorandom Number Generatos)

伪随机数生成序列是基于一个初始化种子值生成的,通常是按照下面的方式递归生成的:

\[s_0 = seed \\ s_{i + 1} = f(s_i),i = 0,1,... \]

伪随机数序列是可以推测出来的,一个例子是在 ANSI C 中的 rand() 函数:

\[s_0 = 12345 \\ s_{i + 1} = 1103515245s_i + 12345 \quad mod \quad 2^{31},i = 0,1,... \]

CSPRNG: Cryptographically Secure Pseudorandom Number Generators

一种不可预测的伪随机数生成器。

2.2.2 单次填充

下面我们会讨论使用三类随机数生成方法生成的密钥对加密的影响。首先定义一个好的密文运算满足:

定义 2.2.1 绝对安全性

如果加密系统即便使用无限的计算资源也不能被破解,那么这个加密系统就是绝对安全的,或可以说是信息论上安全的

绝对安全是基于攻击者具有无穷的计算资源的假设。上面的定义看起来是直白的,但是实现一个绝对安全的加密系统确实不可能的。密文最多能够达到计算上是安全的。

让我们尝试构建一个绝对安全的加密方法,这个方法称作单次填充:

定义2.2.2 单次填充(OTP: One-Time Pad)

  1. 密钥流由真随机数生成器生成
  2. 密钥流只有合法通讯流知道
  3. 每一个密钥流都只使用一次

对于每一个运算比特有:

\[y_0 \equiv x_0 + s_0 \quad mod \quad 2 \\ y_1 \equiv x_1 + s_1 \quad mod \quad 2\\ ... \]

现在我们有一个简单的运算,这个运算是足够安全的。不过这个运算中需要真随机数,这意味着我们需要一个基于白噪声的半导体设备生成真随机数,因为标准计算机并没有真随机数生成器。而第二个需求是,发送发必须有一个绝对安全的方式将这个密钥告知接受发。而第三个要求密钥流不能重用,这就要求我们的密钥与原文的长度相同。

OTPs 几乎不会在实际中应用。不过却给我们一个好的安全运算的设计方法:如果我们将原文与真随机数做异或,我们获取到的密文不会被攻击者轻易破解。

2.2.3 实际的流运算

前面提到的 OTPs 运算是不切实际的,我们尝试使用伪随机数密钥流比特代替真随机数流。在我们实现流运算加密之前,我们需要知道,流运算加密并不是绝对安全的。实际上,所有已知的加密算法(流运算加密,块运算加密,公钥算法)都不是绝对安全的。我们只能期望我们的加密算法是计算安全的:

定义2.2.3 计算安全

如果算法需要至少 t 操作,那么这个加密系统就是运算安全的。

这似乎是一个何理的定义,但是依然有很多问题存在。首先,我们并不知道给定一个攻击,最好的算法是什么。一个典型例子是 RSA 公钥算法,它可以通过因数分解大的整数攻击。即便我们知道一些因数分解算法,但我们并不知道是否存在其他更好的算法。其次,即便一个攻击的算法,但不知道是否存在另一个具有更低复杂度的攻击算法。就像前面介绍过的那样,即使我们知道在穷举法下的攻击复杂度,但还存在其他的更强劲的攻击方法。我们能做的只是设计一种加密算法,令它是计算安全的。对于对称加密,这通常意味着假设其他的攻击方法的难度都不会低于穷举法。

我们仿照 OTPs 算法设计一个算法。只需要交换密钥长度在 100 比特位长的密钥。其他的密钥流则是基于这个种子生成的。显然,我们需要一类随机数生成器来获取密钥流,首先,我们不能使用真随机数发生器,否则发送者与接收者不能生成相同的密钥流。

从 PRNGs 中构建密钥流

一些伪随机数生成器,具有良好的统计特性,这一特性在流运算加密中十分重要。如果我们对密钥流序列做统计测试,输出会是接近于随机的。因此可以假设 PRNG 可以用作生成密钥流。但是这一特性并不充分,考虑下面的例子:

例2.2 我们假设伪随机数基于下面的线性过程生成:

\[S_0 = seed \\ S_{i+1} \equiv AS_i + B \quad mod \quad m,i = 0,1,... \]

我们选择 m 具有 100 比特位长,且:

\[S_i,A,B\in \{0,1,....,m-1\} \]

我们何理选择这几个参数,那么这个真随机数将会具有很棒的统计特性。模数 m 是加密策略的一部分,并且是公开的。与密钥相关的部分 (A,B) 以及种子都是 100 比特位长的。这样,我们就具备了 200 比特位长的密钥,这样的比特位长已经足够对付暴力破解攻击。因为这个算法是流加密算法,那么加密过程可能如下:

\[y_i \equiv x_i + s_i \quad mod \quad 2 \]

其中,x 是原文,y 是密文,s 是加密比特流。

这样的算法很容易被攻击,假设我们知道原文的前 300 比特位的信息(只需要 37.5 字节信息),这可能只是文件头信息。因为已知密文,那么现在我们可以计算前 300 位的密钥流:

\[s_i \equiv y_i + x_i \quad mod \quad m,i = 1,2,...,300 \]

这 300 比特位的数据给到我们 PRNG 的前三个输出:

\[S_1 = (s_1,...,s_100)\\ S_2 = (s_101,...,s_200)\\ S_3 = (s_201,...,s_300) \]

我们可以得到下面式子:

\[S_2 \equiv AS_1 + B \quad mod \quad m \\ S_3 \equiv AS_2 + B \quad mod \quad m \]

这是一个基于正整数环的线性方程,我们可以马上计算得到:

\[A \equiv (S_2 - S_3)/(S_1 - S_2) \quad mod \quad m \\ B \equiv S_2 - S_1(S_2 - S_3)/(S_1 - S_2) \quad mod \quad m \]

如果:

\[gcd((S_1 - S_2), m) \neq 1 \]

我们会有多个解,因为具有基于这个整数环的等效方程。如果知道第四组原文数据,那么密钥在大部分情况下可以唯一确定。或者,我们可以尝试每一组可能的情况。

因此,只要我们知道原文的一部分,我们可以计算密钥并解密整个密文。

使用 CSPRNGs 构建密钥流

2.3 基于移位寄存器的流运算

实际的流运算加密是用密钥比特流,它由密钥流生成器生成,具有特定的属性。一个简单的生成长伪随机序列的方式是使用线性反馈移位寄存器(LFSRs)。LFSRs 通过硬件可以简单实现,但并不是所有的流运算都使用 LFSRs

2.3.1 线性反馈移位寄存器

一个线性反馈寄存器由触发器以及反馈路径组成。触发器的个数给到了 LFSR 的阶数。有几个触发器,就称其为几阶,反馈网络与上一个触发器的输入做异或操作。

例2.3 我们考虑一个 3 阶的 LFSR,具有三个触发器:

\[FF_2,FF_1,FF_0 \]

触发器的内部状态表示为 $$s_i$$。如果我们初始化状态 $$s_2 = 1,s_1 = 0,s_0 = 0$$,下表是所有的 LFSR 序列:

时钟序 $$FF_2$$ $$FF_1$$ $$FF_0= s_i$$
0 1 0 0
1 0 1 0
2 1 0 1
3 1 1 0
4 1 1 1
5 0 1 1
6 0 0 1
7 1 0 0
8 0 1 0

在过了第六个时钟后,就开始重复了,这意味着 3 阶的 LFSR 输出具有下面的周期形式:

\[0010111, 0010111, 0010111 ... \]

有一个简单的公式可以确定这个 LFSR,假设初始序列的为 $$s_0,s_1,s_2$$:

\[s_3 \equiv s_1 + s_0 \quad mod \quad 2 \\ s_4 \equiv s_2 + s_1 \quad mod \quad 2 \\ s_5 \equiv s_3 + s_2 \quad mod \quad 2 \\ ... \]

可以简化为:

\[s_{i+3} \equiv s_{i+1} + s_i \quad mod \quad 2 \]

其中 $$i = 0,1,2...$$

对 LFSRs 的数学描述

一般形式的 LFSR 具有 m 阶的触发器,并具有 m 个可能的反馈,一个反馈通道是否活跃,可以通过反馈系数$$p_0,p_1,...,p_{m-1}$$ 决定:

  • 如果 $$p_i = 1$$ 那么反馈是活跃的
  • 如果 $$p_i = 0$$ 那么对应的反馈是未使用的

让我们假设 LFSR 初始值是 $$s_0,...,s_{m-1}$$,整个系统的输出比特是 $$s_m$$,也是最左侧触发器的输入,有:

\[s_m \equiv s_{m-1}p_{m-1} + ... + s_1p_1 + s_0p_0 \quad mod \quad 2 \]

下一个 LFSR 的输出是:

\[s_{m+1} \equiv s_mp{m-1} + ... + s_2p_1 + s_1p_0 \quad mod \quad 2 \]

实际上输出序列可以描述为:

\[s_{i+m} \equiv \sum_{j = 0}^{m-1} p_j\cdot s_{i+j} \quad mod \quad 2;s_i,p_j \in {0,1};i = 0,1,2,... \]

因为有限的触发器,LFSR 的输出序列是周期重复的。

定理2.3.1 由一个 m 阶的 LFSR 产生的最长序列长度为 $$2^m - 1$$

注意到,全零的状态是需要避免的,因为一旦有这样的状态,LFSR 就绝不会退出这个状态了。注意到,只有何理的配置 $$p_0,...,p_{m-1}$$ 才能够得到 LFSRs 的最大长度。

例2.4 LFSR 生成最长输出序列的例子

给定 LFSR 具阶为 m = 4,且 $$p_3 = 0,p_2 = 0,p_1 = 1,p_0 = 1$$,这样的输出序列周期为 15,达到了最长的序列长度。

例2.5 LFSR 不能生成最长输出序列的例子

给定 LFSR 具阶为 m = 4,且 $$p_3 = 1,p_2 = 1,p_1 = 1,p_0 = 1$$,这样的输出序列周期为 5,没能达到了最长的序列长度。

LFSR 经常视作是下面这样的带有反馈向量的多项式:

\[P(x) = x^m + p_{m-1}x^{m-1} + ... + p_1x + p_0 \]

比如,前面提到的一个系数是 ($$p_3 = 0,p_2 = 0,p_1 = 1,p_0 = 1$$)可以使用 $$x^4 + x + 1$$ 表示。不再展开。

2.3.2 已知明文攻击 LFSRs

顾名思义 LFSR 是线性的。它的输入与输出是线性的。因为线性关系是比较容易进行分析的,在通讯系统中这是它的一个优点。但是,在加密系统中,线性的加密关系是十分不安全的。

如果我们使用 LFSR 做流运算加密,密钥 k 是反馈系数向量 ($$p_{m-1},...,p_1,p_0$$)。如果攻击者知道一些原文以及对应的密文,对这个系统的攻击就是可能的。假设我们知道 LFSR 的阶 m,实际上,攻击者可以尝试很多个可能的 m,因此,我们的假设是可能的。假设我们知道明文 $$x_0,x_1,...,x_{2m-1}$$ 及其对应的密文 $$y_0,y_1,...,y_{2m - 1}$$。使用这些对应的明文密文对,我们可以构建 2m 密钥流:

\[s_i \equiv x_i + y_i \quad mod \quad 2;i = 0,1,...,2m-1 \]

我们的目标是找到由反馈系数 $$p_i$$ 得到的密钥。上面的式子可以总结表示为:

\[s_{i+m} \equiv \sum_{j = 0}^{m - 1}p_j\cdot s_{i+j} \quad mod \quad 2;s_i,p_j \in {0,1};i = 0,1,2... \]

我们对每一个 i 得到一个等式,而且,等式是线性独立的。基于此,我们可以生成 m 个 关于 i 的等式:

\[i = 0,s_m \equiv p_{m-1}s_{m-1} + ... + p_1s_1 + p_0s_0 \quad mod \quad 2 \\ i = 1,s_{m+1} \equiv p{m-1}s_m + ... + p_1s_2 + p_0s_1 \quad mod \quad 2 \\ ... \\ i = m -1,s_{2m - 1} \equiv p_{m_1}s_{2m-2} + ... + p_1s_m + p_0s_{m-1} \quad mod \quad 2 \]

现在我们有了 m 个线性等式,可以通过高斯消元法解出系数。即便 m 是一个非常大的值,现代计算机也可以轻易求解。

2.3.3 Trivium 算法

Trivium 算法是具有 80 比特位密钥的流运算,基于三个移位寄存器组合。即便这些是反馈移位寄存器,有非线性组件用来得到每个寄存器的输出。

Trivium 描述

Trivium 的核心是三个移位寄存器,A、B 与 C,分别是 93、84 以及 111。三个寄存器的异或和组成密钥流 $$s_i$$。这种运算的一个特性是每一个寄存器的输出会连接到另一个寄存器的输入上。因此,寄存器就像是环一样组织的。这个运算,可以看作由 93 + 84 + 111 = 288 总长组成的环寄存器。这三个寄存器具有相似的结构描述如下。

每一个寄存器的输入都是由两个比特位的异或和组成:

  • 另一个移位寄存器的输出,比如 A 的输出是 B 的输入
  • 某个寄存器位做为另一个输入,比如 A 的比特位 68 位反馈输入给自己

总的输出由三个比特位的异或和组成:

  • A,B,C 的最右侧比特位
  • A,B,C 寄存器的某一比特位的前馈
  • A,B,C 寄存器的某两位的按位与并前馈

比如下面这样的 Trivium:

寄存器 寄存器长 反馈比特 前馈比特 按位与比特
A 93 69 66 91,92
B 84 78 69 82,83
C 111 87 66 109,110

按位与等价于模 2 算术中的乘法。如果我们对两个未知的项做乘法,且攻击者想要恢复的寄存器内容也是未知的情况下,得到的等式就不再是线性的,因为它包含两个未知项。因此,按位与的前馈是 Trivium 安全的关键。

使用 Trivium 加密

几乎所有的流运算都有两个输入参数,一个密钥 k 以及一个初始化向量 IV。k 是常规的密钥,用在每一个对称加密系统中。IV 做为一个随机数,在每次加密会话时都选一个新的随机数。需要注意的是 IV 没有必要保密,只需要在每个会话时做修改。IV 只用一次,它的主要作用是在运算时的密钥流要不同,即便密钥没有改变过。如果攻击者知道第一个密文的明文,它可以计算出对应的密钥流。而使用相同密钥流加密的密文可以马上被解密。不修改 IV,流运算加密是确定性的。

下面我们看一下 Trivium 的运行细节吧。

初始化

80 比特位的初始化向量载入到 A 寄存器的最左侧的寄存器位置,80 位的密钥载入到 B 寄存器的最左侧位置,除了 C 寄存器的最右侧三个寄存器比特位设置为 1,其他所有的寄存器都初始化为 0。

热身

首先,跳动 $$4\times288 = 1152$$ 次时钟,这个阶段输出无效。

加密

从第 1153 个时钟输出的比特为密钥流的起始位。

热身阶段是必须的,它可以令输出足够随机化。且密钥流与 k、IV 相关。

posted @ 2023-02-20 21:59  ArvinDu  阅读(71)  评论(0编辑  收藏  举报