随机数

随机数是专门的随机试验的结果。

产生随机数有多种不同的方法,这些方法被称为随机数发生器。

根据密码学原理,随机数的随机性检验可以分为三个标准:

  1. 统计学伪随机性。统计学伪随机性指的是在给定的随机比特流样本中,1的数量大致等于0的数量,同理,“10”“01”“00”“11”四者数量大致相等。类似的标准被称为统计学随机性。满足这类要求的数字在人类“一眼看上去”是随机的。

  2. 密码学安全伪随机性。其定义为,给定随机样本的一部分和随机算法,不能有效的演算出随机样本的剩余部分。

  3. 真随机性。其定义为随机样本不可重现。实际上只要给定边界条件,真随机数并不存在,可是如果产生一个真随机数样本的边界条件十分复杂且难以捕捉(比如计算机当地的本底辐射波动值),可以认为用这个方法演算出来了真随机数。

相应的,随机数也分为三类:

  1. 伪随机数:满足第一个条件的随机数。
  2. 密码学安全的伪随机数:同时满足前两个条件的随机数。可以通过密码学安全伪随机数生成器计算得出。
  3. 真随机数:同时满足三个条件的随机数。

Linux随机数

在Linux内核中采用熵来描述数据的随机性。为了能产生足够高效随机数,Linux系统维护了一个专门用于收集噪声的熵池(entropy pool),熵池的信息来自外界的熵信息 如:设备驱动的噪音,鼠标点击的声音,设备风扇运作的噪音。将这些噪声收集起来被用于产生真正的随机数。如果熵池中没有足够的噪音资源,就需要等待熵池的收集。只要设备的物理混乱程度越大,熵增就越快,熵池的信息越饱和,随机性的效率就越高。

/dev/random 是 Linux/Unix 操作系统中的一个设备文件,用来作为随机数发生器/伪随机数发生器。它允许程序访问来自设备驱动程序或其它来源的背景噪声,Linux是第一个以背景噪声产生真正的随机数的实现。这种设计使得/dev/random是真正的随机数发生器,提供了最大可能的随机数据熵。因为它是真正以熵池的背景噪音为种子产生随机数,所以在调用时可能会有堵塞,需要等待熵池有足够多的熵信息。另外一种 /dev/urandom ,它是非阻塞的发生器,它会重复使用熵池中的信息产生伪随机数。这种随机数的生成一般运用在密钥强度要求不是太高的地方。另外还有一些常见的随机数算法有线性同余法(Linear Congruential Generator)、梅森旋转法(Mersenne twister)等。其中线性同余的随机性相对较差点,而梅森旋转法的随机性较好,ruby、python的底层实现采用了这种随机数算法。

概述

通常从设备驱动程序等收集环境噪音,并返回适合加密用途的良好随机数。除了明显的密码用途外,这些数字还适用于播种 TCP 序列号,以及其他需要数字的地方,这些数字不仅是随机的,而且攻击者难以预测。

操作原理

计算机是非常可预测的设备。因此,在计算机上产生真正的随机数是极其困难的——与伪随机数相反,伪随机数可以很容易地通过算法产生。不幸的是,攻击者很容易猜出伪随机数生成器的序列,对于某些应用程序来说,这是不可接受的。因此,我们必须尝试从计算机环境中收集外部攻击者难以观察到的“环境噪声”,并使用它来生成随机数。在 Unix 环境中,这最好从内核内部完成。

来自环境的随机性来源包括键盘间计时、来自某些中断的中断间计时,以及其他 (a) 不确定且 (b) 外部观察者难以测量的事件。来自这些来源的随机性被添加到“熵池”中,该池使用类似 CRC 的函数进行混合。这在密码学上并不强大,但假设不是恶意选择随机性就足够了,而且速度足够快,每次中断时这样做的开销是非常合理的。当随机字节混合到熵池中时,例程会估计*有多少随机位已存储到随机数生成器的内部状态中。

当需要随机字节时,它们是通过对“熵池”的内容进行 SHA 哈希来获得的。 SHA 哈希避免暴露熵池的内部状态。人们认为从 SHA 的输出中导出任何关于 SHA 输入的有用信息在计算上是不可行的。即使有可能以某种巧妙的方式分析 SHA,只要从生成器返回的数据量小于池中的固有熵,输出数据就完全不可预测。出于这个原因,该例程在输出随机数时会降低其对熵池中包含多少“真正随机性”位的内部估计。

如果这个估计值变为零,例程仍然可以生成随机数;然而,攻击者可能(至少在理论上)能够从先前的输出中推断出生成器的未来输出。这需要对 SHA 进行成功的密码分析,这被认为是不可行的,但可能性很小。尽管如此,这些数字对于绝大多数用途应该是有用的。

导出接口

4个接口,2个内核使用,2个用户态使用

用户态接口

/dev/random 安全性更高,如果熵不足会阻塞。

/dev/urandom 不阻塞。

内核接口

get_random_bytes 《==》 /dev/urandom

  • u32 get_random_u32()
    
  • u64 get_random_u64()
    
  • unsigned int get_random_int()
    
  • unsigned long get_random_long()
    

这些是由从 get_random_bytes 播种的加密 RNG 生成的,因此不会耗尽熵池。如果结果要存储在内核中*,建议将这些用于大多数内核操作。

具体来说,get_random_int() 系列不会尝试进行“反回溯”。如果您捕获内核的状态(例如通过快照 VM),您可以计算出以前的 get_random_int() 返回值。但是如果值无论如何都存储在内核中,这不是问题。

将 get_random_int() 输出暴露给攻击者是*安全的(例如作为网络 cookie);给定输出 1..n,预测输出 0 或 n+1 是不可行的。唯一需要担心的是稍后闯入内核的攻击者; get_random_int() 引擎不像 get_random_bytes() 那样经常重新播种。

get_random_bytes() 对于从内核中删除后需要保密的密钥是必需的。例如,任何将被包装并加密存储的密钥。和会话加密密钥:我们想知道在会话关闭并删除密钥后,明文对于记录密文的人来说是不可恢复的。

但是对于网络端口/cookie、堆栈金丝雀、PRNG 种子、地址空间布局随机化、sessionauthentication* 密钥,或敏感数据以明文形式存储在内核中的其他应用程序,只要它是敏感的,get_random_int() 系列就只是美好的。

考虑 ASLR。我们希望在进程运行时对外部攻击者保密地址空间,但是一旦地址空间被拆除,它对攻击者就不再有用了。只要它还存在,它就会存储在内核数据结构中,因此担心攻击者有能力从 get_random_int() CRNG 推断它是愚蠢的。

使用 get_random_int() 甚至可以安全地生成一些加密密钥。特别是 SipHash 的密钥通常没问题。在这里,密钥的知识授权您对内核对象执行某些操作(将数据包注入网络连接,或淹没哈希表),并且密钥与受保护的对象一起存储。一旦它消失了,我们就不再关心是否有人知道密钥。

prandom_u32()

对于更弱的应用程序,请参阅伪随机生成器 prandom_u32()、prandom_max() 和 prandom_bytes()。如果随机数根本不是安全关键的,那么这些随机数要便宜得多。可用于自测、随机错误模拟、随机退避以及您相信没有人试图通过猜测“随机”数字来恶意干扰您的任何其他应用程序。

参考资料

维基百科:https://zh.m.wikipedia.org/zh-hans//dev/random

随机数评估

https://www.zhihu.com/question/20222653

https://www.fourmilab.ch/random/

随机数相关概念

硬件随机数生成器

硬件随机数生成器(英语:hardware random number generator),或真随机数生成器(True Random Number Generator,縮寫:TRNG)是一种通过物理过程而不是计算机程序来生成随机数的设备。

密码学安全伪随机数生成器

密码学安全伪随机数生成器(亦作密码学伪随机数生成器,英文:Cryptographically secure pseudo-random number generator,通称CSPRNG),是一种能够通过运算得出密码学安全伪随机数伪随机数生成器。相较于统计学伪随机数生成器和更弱的伪随机数生成器,CSPRNG所生成的密码学安全伪随机数具有额外的伪随机属性。

加密随机数生成器

cryptographic random number generator(CRNG)

真随机数

不同的地方不同叫法,没有绝对。

haveged

可以增加噪声来源。

jitter-RNG

提供接口生成密码学安全的伪随机数

参考资料

1)https://baike.baidu.com/item/%E9%9A%8F%E6%9C%BA%E6%95%B0