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

运作理论

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

来自环境的随机数源包括键盘输入,来自某些中断的触发时间,以及(a)非确定性和(b)外部观察者难以测量的其他事件。来自这些源的随机数被添加到“熵池”,其使用类似CRC的函数混合。这在加密方面并不强大,但是假设不是恶意选择随机性就足够了,并且它足够快以至于在每次中断上执行它的开销非常合理。当随机字节被混合到熵池中时,这个程序会评估随机数发生器的内部状态中存储了多少位随机数。

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

如果此估计值为零,则例程仍然可以生成随机数;但是,攻击者可能(至少在理论上)是能够从之前推断出发生器的未来输出。这需要成功的SHA密码分析,即不相信是可行的,但有一个遥远的可能性。尽管如此,这些数字应该对绝大多数目的有用。

导出的接口----输出

有三个导出的接口;第一个是设计用于内核中的一个:

void get_random_bytes(void * buf,int nbytes);

此接口将返回请求的随机字节数,并将其放在请求的缓冲区中。

另外两个接口是两个字符设备/dev/random和/dev/urandom。/dev/random适用于需要非常高质量的随机性时(例如,用于密钥生成或一次性填充),因为它只返回随机性的包含在熵池中最大位数(由随机数生成器估计)。

/dev/urandom设备没有此限制,并将返回所请求的字节数。随着越来越多的随机字节被请求而没有给熵池充电的时间,这将导致仅仅加密强度的随机数。然而,对于许多应用来说,这是可以接受的。

导出的接口----输入

用于从设备收集环境噪声的当前导出接口是:

void add_device_randomness(const void * buf,unsigned int size);
void add_input_randomness(unsigned int type,unsigned int code,unsigned int value);
void add_interrupt_randomness(int irq,int irq_flags);
void add_disk_randomness(struct gendisk * disk);

add_device_randomness()用于将数据添加到随机池中,这可能在两个设备之间有所不同(甚至可能在每次启动时)。这可能是MAC地址或序列号,或RTC的读出。这不会*向池中添加任何实际的熵,但它会将池初始化为不同的设备值,否则这些设备可能相同并且可用的熵非常少(在嵌入式世界中尤为常见)。

add_input_randomness()使用输入层中断时间,以及来自硬件的事件类型信息。

add_interrupt_randomness()使用中断时间作为熵池的随机输入。使用循环计数器和irq源作为输入,它大约每秒提供一次随机性。

add_disk_randomness()在每个disk_devt的基础上使用相当于块层请求事件的查找时间作为熵池的输入。请注意,寻道时间非常短的高速固态驱动器无法获得良好的熵源,因为它们的寻道时间通常相当一致。

所有这些例程都试图估计特定随机源的随机数位数。他们通过跟踪事件时间的一阶和二阶增量来实现这一点。

确保系统启动时的不可预测性

当任何操作系统启动时,它将经历一系列对手可以预测的动作,特别是如果启动不涉及与人类操作员的交互。这将熵池中不可预测性的实际位数减少到entropy_count中的值以下。为了抵消这种影响,有助于在关闭和启动过程中携带熵池中的信息。为此,请将以下行放在引导序列期间运行的相应脚本中:

 echo "Initializing random number generator..."
 random_seed=/var/run/random-seed
 # Carry a random seed from start-up to start-up
 # Load and then save the whole entropy pool
 if [ -f $random_seed ]; then
 	cat $random_seed >/dev/urandom
 else
 	touch $random_seed
 fi
 chmod 600 $random_seed
 dd if=/dev/urandom of=$random_seed count=1 bs=512

以及在系统关闭时运行的相应脚本中的以下行:

 # Carry a random seed from shut-down to start-up
 # Save the whole entropy pool
 echo "Saving random seed..."
 random_seed=/var/run/random-seed
 touch $random_seed
 chmod 600 $random_seed
 dd if=/dev/urandom of=$random_seed count=1 bs=512

例如,在大多数使用System V init脚本的现代系统中,这些代码片段可以在/etc/rc.d/init.d/random中找到。在较旧的Linux系统上,正确的脚本位置可能位于/etc/rcb.d/rc.local或/etc/rc.d/rc.0中。

实际上,这些命令导致熵池的内容在关闭时保存并在启动时重新加载到熵池中。 (除了启动脚本之外,'dd’是为了确保/ etc / random-seed对于每次启动都是不同的,即使系统在没有执行rc.0的情况下崩溃。)即使完全了解启动特征活动,预测熵池的状态需要知道系统的先前历史。

在Linux下配置/ dev / random驱动程序

Linux下的/dev/random驱动程序使用/dev/mem主编号(#1)的次要编号8和9。因此,如果您的系统没有已创建/dev/random和/dev/urandom,则可以使用以下命令创建它们:

mknod /dev/random c 1 8
mknod /dev/urandom c 1 9

更多推荐

A strong random number generator(linux强随机数生成器)