Linux下的随机数生成器-rng
2021-03-13 21:47:36 阿炯

随机数生成器(Random Number Generator)是通过一些算法、物理讯号、环境噪音等来产生看起来似乎没有关联性的数列的方法或装置。如丢硬币、丢骰子、洗牌就是生活上常见的随机数产生方式。大部分计算机上的伪随机数,并不是真正的随机数,只是重复的周期比较大的数列,是按一定的算法和种子值生成的。


为了能够生成安全的不被轻易破解的加密密钥,需要一个随机数源。通常数字随机性越大,越能够得到更佳的唯一密钥。用于生成随机数的熵是通常从计算机环境"噪声"获得,或者使用硬件的随机数生成器。

rngd服务,是rng-tools软件包的部分,能够使用环境噪声和硬件随机数生成器来生成熵。这个服务检查是否有充分随机的随机性源来提供数据并存储它到内核的随机数熵池(random-number entropy pool)。这个随机数是通过/dev/random和/dev/urandom字符设备来生成的。

随机数生成器(Random Number Generator, RNG)是生成伪随机数的机制。伪随机数字可以用于生成SSH key,进程随机PID,TCP序列号以及UUID等。使用加密(文件系统,邮件等等)需要大量的伪随机数。

要允许应用程序获得伪随机数,至少有2个内核块设备:

/dev/urandom:提供标准质量的随机数,不管是否有熵都可以工作:当需要大量的随机数的时候,随机性质量会下降(这是因为/dev/urandom设备是一个不中断源,会重用内核熵池,所以提供的是无限的伪随机数)。

/dev/random:提供高质量随机数,但是缺乏熵的时候会停止工作:在Linux内核保留了一个4KB的熵池(entropy pool)(这个熵池的大小见/proc/sys/kernel/random/poolsize);当这个pool空的时候,内核阻塞。

为了得到优质的伪随机数,需要一些熵(entropy)。这个熵是由操作系统搜集的随机性因素:
键盘实践,网络流量,鼠标移动,中断,ide计时等内部源
在Intel IvyBridge和Haswell处理器提供的RDRAND这种特定处理器指令
通过virtio-rng半虚拟化设备实现虚拟机随机设备(参考Red Hat文档Access to Random Numbers Made Easy in RHEL7)
独立的、外部、物理设备(称为TPM,即Trusted Platform Module)



以下实践是为了在KVM虚拟机中安装CentOS内核源代码,解决数字签名使用到的随机数生成问题,部署安装rng的实践。


rng-tools软件包

以下命令安装rng-tools
yum install rng-tools

启动及检查rngd服务(CentOS 7)
systemctl start rngd
systemctl status rngd

rngd也支持指定随机数设备输入(默认是/dev/hwrandom)
rngd --rng-device=/dev/hwrng

或者-o或--random-device指定选择内核随机数输出(默认是/dev/random)。

rng-tools软件包也包含了rngtest工具,可以用来检查数据的随机性,要检测/dev/random的输出随机性水平,可以使用rngtest工具如下:
cat /dev/random | rngtest -c 1000


虚拟机中部署rng

这个/sbin/rngd属于软件包 rng-tools,可以通过 sudo yum install rng-tools,但是在kvm虚拟机中执行:
sudo rngd -r /dev/hwrandom

提示错误

Unable to open file: /dev/tpm0
can't open any entropy source
Maybe RNG device modules are not loaded

尝试加载sudo modprobe intel-rng,但是提示错误

FATAL: Error inserting intel_rng (/lib/modules/2.6.32-696.el6.x86_64/kernel/drivers/char/hw_random/intel-rng.ko): No such device

尝试启动rngd服务,提示缺少/dev/tpm0设备文件

$ sudo /etc/init.d/rngd start
Starting rngd: Unable to open file: /dev/tpm0
can't open any entropy source
Maybe RNG device modules are not loaded

参考:RHEL7: How to get started with random number generator.


CentOS 7 rng

对于虚拟机,需要先增加virtio_rng设备
virsh shutdown centos7_vm
virsh edit centos7_vm

添加

<rng model='virtio'>
<rate bytes='1024' period='1000'/>
<backend model='random'>/dev/random</backend>
</rng>

然后再次启动,发现启动后centos7_vm内部的rngd进程状态也是D住的。

$ ps aux | grep rng
root       523  0.0  0.0      0     0 ?        D    11:17   0:00 [hwrng]
root       635  0.0  0.0   4368   584 ?        Ds   11:17   0:00 /sbin/rngd -f

检查服务启动日志

$ systemctl status rngd -l
● rngd.service - Hardware RNG Entropy Gatherer Daemon
   Loaded: loaded (/usr/lib/systemd/system/rngd.service; enabled; vendor preset: enabled)
   Active: active (running) since Fri 2017-04-14 11:17:56 CST; 2min 21s ago
 Main PID: 635 (rngd)
   CGroup: /system.slice/rngd.service
           └─635 /sbin/rngd -f

Apr 14 11:17:56 dev7 systemd[1]: Started Hardware RNG Entropy Gatherer Daemon.
Apr 14 11:17:56 dev7 systemd[1]: Starting Hardware RNG Entropy Gatherer Daemon.

检查了虚拟机内部,已经生成了/dev/random和/dev/urandom设备

$ ls -lh /dev/random
crw-rw-rw-. 1 root root 1, 8 Apr 14 11:17 /dev/random

$ ls -lh /dev/urandom
crw-rw-rw-. 1 root root 1, 9 Apr 14 11:17 /dev/urandom

检查rng

# systemctl stop rngd
# rngd -v
Available entropy sources:
    Intel/AMD hardware rng

这里需要先停止rngd然后才能使用rngd -v检查,否则会卡住。

在KVM物理服务器,可以使用如下命令检查有哪些虚拟机使用了virt-rng设备。

lsof /dev/random | grep qemu-kvm | awk '{print $2;}' | xargs ps -c | awk '{print $9;}'

再次CentOS 7 虚拟机,发现重启后 rngd服务启动失败。

● rngd.service - Hardware RNG Entropy Gatherer Daemon
   Loaded: loaded (/usr/lib/systemd/system/rngd.service; enabled; vendor preset: enabled)
   Active: failed (Result: exit-code) since Fri 2017-04-14 11:32:01 CST; 3min 47s ago
  Process: 641 ExecStart=/sbin/rngd -f (code=exited, status=1/FAILURE)
 Main PID: 641 (code=exited, status=1/FAILURE)

Apr 14 11:32:01 dev7 systemd[1]: Started Hardware RNG Entropy Gatherer Daemon.
Apr 14 11:32:01 dev7 systemd[1]: Starting Hardware RNG Entropy Gatherer Daemon...
Apr 14 11:32:01 dev7 rngd[641]: Unable to open file: /dev/tpm0
Apr 14 11:32:01 dev7 rngd[641]: can't open any entropy source
Apr 14 11:32:01 dev7 rngd[641]: Maybe RNG device modules are not loaded
Apr 14 11:32:01 dev7 systemd[1]: rngd.service: main process exited, code=exited, status=1/FAILURE
Apr 14 11:32:01 dev7 systemd[1]: Unit rngd.service entered failed state.
Apr 14 11:32:01 dev7 systemd[1]: rngd.service failed.

这是因为默认使用的/dev/tpm0设备不存在。需要修改配置让rngd不要使用硬件熵源:
cp /usr/lib/systemd/system/rngd.service /etc/systemd/system

修改/etc/systemd/system/rngd.service,替换:
ExecStart=/sbin/rngd -f -r /dev/urandom

然后重新加载systemd
systemctl daemon-reload

再次启动rngd
systemctl restart rngd

然后检查就可以看到rngd服务工作正常。

haveged服务

HAVEGE是一个从内部CPU硬件状态(缓存,分支预测因子,TLBs)作为不确定根源的随机数生成器,可作为rgnd的替代。这个软件包是通过EPEL仓库提供:
yum install epel-release
yum install haveged


测试

要了解熵池的补充数度有多快,可以使用如下命令:
watch -n 1 cat /proc/sys/kernel/random/entropy_avail

注意:由于4KB的熵池限制,你不能获得大于4095的值。

测试当前熵池源的质量,使用命令:
cat /dev/random | rngtest -c 1000

虚拟机内部使用rngd经验小结

通过virsh edit VM编辑KVM虚拟机的配置,增加
<rng model='virtio'>
<backend model='random'>/dev/random</backend>
</rng>

虚拟机重启后会在虚拟机内部增加/dev/random和/dev/urandom两个设备,需要配置rngd服务启动参数,增加-r /dev/urandom,否则会导致rngd进程启动后D住。
CentOS 6 修改/etc/sysconfig/rngd设置参数EXTRAOPTIONS="-r /dev/urandom"
CentOS 7 复制配置文件cp /usr/lib/systemd/system/rngd.service /etc/systemd/system,并修改ExecStart=/sbin/rngd -f -r /dev/urandom


为何需要rng和虚拟化virtio-rng

在KVM虚拟机中安装CentOS内核源代码时,你会发现需要生成PGP密钥来给模块签名,此时系统会提示需要rngd需要读取/dev/random或/dev/urandom设备来生成密钥。如果libvirtd创建的虚拟机没有包含这个虚拟随机数生成器设备,会导致安装过程卡住长时间无法结束。在KVM虚拟机中运行FreeBSD系统,启动虚拟机时,如果你仔细观察启动信息,会看到FreeBSD启动时提示kernel: random device not loaded; using insecure entropy。这也是因为KVM虚拟机没有提供虚拟随机数生成器导致的。


参考来源:
CentOS下随机数生成器rng