十二、随机数——不可预测性的源泉

1 骡子的锁匠铺

很久很久之前,骡子开了一家锁匠铺,他说:“我做的锁头很坚固,小偷绝对打不开。”因此动物村里所有的动物都为自己的房子装上了骡子做的锁。
骡子做的锁确实很坚固,但是每把锁头上用的钥匙居然都是同一个形状的。因此小偷只要得到了一栋房子的钥匙,就可以打开所有房子的锁了。
教训:坚固的锁头固然重要,但不可预测的钥匙更加重要。

2 本章概要

  • 使用随机数的密码技术
  • 随机数的性质
  • 伪随机数生成器
  • 具体的伪随机数生成器
  • 对伪随机数生成器的攻击

3 使用随机数的密码技术

3.1 随机数是干什么的

  1. 生成秘钥:用于对称密码和消息认证码。
  2. 生成密钥对:用于公钥密码和数字签名。
  3. 生成初始化向量(IV):用于分组密码的 CBC、CFC 和 OFB 模式。
  4. 生成 nonce:用于防御重放攻击以及分组密码的 CTR 模式等。
  5. 生成盐:用于基于口令的密码(PBE)等。

在这里,请大家记住为了不让攻击者看穿而使用随机数这一观点,因为“无法看穿”,及不可预测性,正是本章的主题。

4 随机数的性质

4.1 对随机数的性质分类

  1. 随机性:不存在统计学偏差,是完全杂乱的数列。
  2. 不可预测性:不能从过去的数列推测出下一个出现的数。
  3. 不可重现性:除非将数列本身保存下来,否则不能重现相同的数列。

为了方便起见,将上述三个性质按顺序分别命名为“弱伪随机数”、“强伪随机数”和“真随机数”。
||随机性|不可预测性|不可重现性||
|:-:|:-:|:-:|:-:|:-:|
|弱伪随机数|✔️|✘|✘|只具备随机性|
|强伪随机数|✔️|✔️|✘|具备不可预测性|
|真随机数|✔️|✔️|✔️|具备不可重现性|

4.2 随机性

杂乱无章并不代表不会被看穿,因此本书中将只具备随机性的伪随机数称为“弱伪随机数”。

4.3 不可预测性

不可预测性(unpredictability),是一种“不可能事先说中”的性质,及不可预测性。即,攻击者在知道过去生成的为随机数列的前提下,依然无法预测出下一个生成出来的伪随机数的性质。

4.4 不可重现性

即,无法重现和某一随机数列完全相同的数列的性质。如果除了将随机数列本身保存下来意外,没有其它方法能够重现该数列,则我们就说该随机数列具备不可重现性。
要生成具备不可重现性的随机数列,需要从不可重现的物理现象中获取信息,比如周围的温度和声音的变化、用户移动的鼠标的位置信息、键盘输入的时间间隔、放射线测量仪的输出值等,根据从这些硬件中获取的信息而生成的数列,一般可以认为是具备不可重现性的随机数列。

5 伪随机数生成器

仅仅靠软件无法生成真随机数,因此要加上一个“伪”。

5.1 伪随机数生成器的结构

伪随机数生成器具有“内部状态”,并根据外部输入的“种子”来生成伪随机数列。

1.伪随机数生成器的内部状态

伪随机数生成器的内部状态,是指伪随机数生成器所管理的内存中的数值。伪随机数生成器会根据内存中的数值进行计算,并将计算的结果作为伪随机数输出。随后,为了响应下一个伪随机数请求。伪随机数生成器会改变自己的内部状态。

2.伪随机数生成器的种子

伪随机数的种子是用来对伪随机数生成器的内部状态进行初始化的。

6 具体的伪随机数生成器

6.1 杂乱的方法

用一个程序员都不懂的算法生成,但是这是错误的,不能用于密码技术。因为,周期太短,使用复杂算法所生成的数列大多数都会具有很短的周期(即短数列的不断重复)。另外则是,无法判断所生成的随机数是否具备不可预测性。

6.2 线性同余法

线性同余法(linear congruential method)是一种使用很广泛的伪随机数生成器算法。然而,它并不能用于密码技术。
A、C、M 都是常量,且 A 和 C 需要小于 M。接下来,根据种子 R0 计算下一个伪随机数 R1:
R1 = (A R0 + C) mod M。
当前得到的伪随机数即是下一个伪随机数的种子:
Rn+1 = (A
Rn + C) mod M。
但是这具有周期性,而且可以通过上一个种子得到下一个伪随机数。因此不具备不可预测性,不能将线性同余法用于密码技术。
很多伪随机数生成器的库函数都是采用线性同余法编写的。包括 C 语言的库函数 rand。 以及 java 的 java.util.Random 类等。

6.3 单向散列函数法


攻击者要预测下一个伪随机数,需要知道计数器的当前值,以及,破解单向散列函数的单向性。利用了单向散列函数的单向性。

6.4 密码法


密码的机密性是支撑伪随机数生成器不可预测性的基础。

7 对伪随机数生成器的攻击

7.1 对种子进行攻击

7.2 对随机数池进行攻击

一般不会到了需要的时候才当场生成真随机数,而是会事先在一个名为随机数池(random pool)的文件中积累随机比特序列。当密码软件需要伪随机数的种子时,可以从这个随机数池中取出所需长度的随机比特序列来使用。这是不能被攻击所知道的。

8 本章小结

由于密码技术的伪随机数生成器,需要使用单向散列函数和密码等技术来确保不可预测性。

9 小测验

  1. 伪随机数的种子需要对攻击者保密。
  2. 线性同余法可以作为用于密码的伪随机数生成器。
  3. 具备随机性的伪随机数生成器不一定具备不可预测性。