说到随机,有两个必须搞清楚的概念:真随机数生成器(TRNG)和伪随机数生成器(PRNG)。
伪随机数是用确定性的算法计算出来的均匀分布的随机数序列,并不真正随机,但是具有类似于随机数的统计特征,如均匀性、独立性等。在计算伪随机数时,若使用的初值(种子)不变,那么伪随机数的数序也不变。伪随机数可以用计算机大量生成。在模拟研究中为了提高模拟效率,一般采用伪随机数代替真正的随机数。模拟中使用的一般是循环周期极长、并能通过随机数检验的伪随机数,以保证计算结果的随机性。
大部分计算机程序和语言中的随机函数都是伪随机数生成器,都是由确定的算法通过一个“种子”(比如“时间”)来产生的、“看起来如果再次运行,会发现上面一段随机数变了,下面一段则“涛声依旧随机”的结果。毫无疑问,任何人只要知道算法和种子,或者之前已经产生了的随机数,都可能获得接下来随机数序列的信息。因为它们可预测,在密码学上并不安全,所以称其为“伪随机”。这种随机数用来让游戏里的小人跑路是没多大问题的,如果用来生成私钥就太不安全了。
严格意义上的真随机可能仅存在于量子力学之中,我们当前所想要的(或者所能要的)并不是这种随机。我们其实想要一种不可预测、统计意义上、密码学安全的随机数,只要能做到这一点的随机数生成器都可以称为真随机数生成器。
想直接利用算法得到真随机数有点难度,但是也不是没有办法。计算机不能产生随机数,但是现实世界中有非常多的随机因素,将这种随机因素引入计算机就能实现真随机。
Linux内核中的随机数发生器(/dev/random)理论上能产生真随机,即这个随机数的生成独立于生成函数,我们说这个随机数发生器是非确定的(不可预见的)。这种真随机并不一定非得是特殊设计的硬件。
Linux操作系统内核中的随机数生成器(/dev/random)维护了一个熵池,用于搜集硬件噪声,比如键盘敲击速度、鼠标位置变化、网络信号强度变化、时钟、IO请求的响应时间、特定硬件中断的时间间隔甚至周围的电磁波等。直观地讲,按一次键盘、动一下鼠标、邻居家Wi-Fi信号强度变化、磁盘写入速度等都能够提供最大可能的随机数据熵。
Linux维护着这样一个熵池,不断收集非确定性的设备事件来作为种子(种子就是输入到随机数产生器中的比特串),因此可认为是高品质的真随机数生成器。不过/dev/random是阻塞的,也就是说,如果熵池空了,对于/dev/random的读操作将被挂起,直至收集到足够的环境噪声为止。
因此,在开发程序时,我们应使用/dev/urandom作为/dev/random的一个副本,它不会阻塞,但其输出的熵可能会小于/dev/random。真随机数有一个非常基本的特征就是不可预测性。