随机数生成
- 随机数生成器 (RNG) 是一种计算或物理设备,旨在生成一系列缺乏任何模式的数字或符号,即看起来是随机的。
随机数据的生成对于多种应用至关重要,例如生成加密密钥(例如用于静态数据加密)、安全擦除磁盘、运行加密的 软件接入点。
内核内置 RNG
Linux 内核的内置 RNG 生成密码学安全的伪随机数据。它的工作原理是从各种来源收集熵,例如硬件 RNG、中断和基于 CPU 的抖动熵。没有单独依赖任何单一的熵源。熵使用 BLAKE2s 密码学哈希函数 提取,并用于播种一组 ChaCha20 CRNG(密码学随机数生成器),这些 CRNG 提供实际的随机数据。只要内核正在运行,熵就会持续收集,并且 CRNG 会定期重新播种。
Linux RNG 为用户空间提供三个接口来获取随机数据
- getrandom(2) 系统调用
/dev/random
/dev/urandom
历史上,/dev/random
被认为比 /dev/urandom
提供更强的随机数。然而,/dev/random
和 /dev/urandom
的行为随着时间的推移已经显著趋同,并且在 x86-64 系统上,它们现在是等效的。由于 Arch Linux 仅支持 x86-64,因此 /dev/random
和 /dev/urandom
在 Arch Linux 上是等效的。(这是真的,因为所有 x86-64 CPU 上都存在 RDTSC 指令,这意味着 基于 CPU 的抖动熵算法 始终能够生成熵,更不用说大多数 x86-64 CPU 也支持 RDRAND。)
在其他架构(特别是没有快速循环计数器的架构)上,两者之间仍然存在一个区别:/dev/random
会阻塞,直到内核估计 CRNG 已正确初始化,而 /dev/urandom
则不会。因此,在一般情况下,用户和应用程序仍然应该遵循使用 /dev/random
生成长期加密密钥的传统指导,或者使用默认行为类似于 /dev/random
的 getrandom(2)。
请注意,由于 /dev/random
不再旨在提供真正的随机数据,而是“仅仅”提供密码学安全的随机数据(这对于所有实际用例都已足够),因此从中读取数据不再会导致内核的熵池耗尽。因此,/proc/sys/kernel/random/entropy_avail
应该始终包含 256,这是 ChaCha20 密钥的大小(以位为单位)。可以忽略期望此文件中存在更大值,或者期望用户在值“过低”时采取行动的历史文档。
替代方案
不需要密码学安全随机数的应用程序可以简单地使用非密码学随机数生成器,例如 random(3)。
对于确实需要密码学安全随机数的应用程序,通常不需要内核 RNG 以外的任何东西。历史上,内核 RNG 相当慢,并且没有利用尽可能多的熵源。然而,它已经得到改进,在 x86-64 上提供约 400 MB/s 的吞吐量,并利用更多的熵源。即使对于需要相当高吞吐量随机数的情况,例如安全擦除磁盘,简单地从 /dev/urandom
读取也可以正常工作。
可能不直接使用内核 RNG 的情况包括
- 在极少数情况下,应用程序需要具有非常高吞吐量或非常低延迟的密码学安全随机数,而系统调用的开销是无法容忍的。用户空间 CRNG 可以解决这个问题。用户空间 CRNG 应该从内核 RNG 播种,以确保它们至少与内核 RNG 一样安全,但需要考虑重新播种等因素。
- 一些应用程序在已经为使用用户空间 CRNG 生成密码学安全随机数建立了完善 API 的领域中工作。例如,当应用程序使用 OpenSSL 时,可以使用 OpenSSL 的 RAND_bytes(3) 可能更有意义。当应用程序用 Java 编写时,使用
java.security.SecureRandom
可能更有意义。通常,这些 API 底层的用户空间 CRNG 会自动使用内核 RNG 进行播种。
- 如果您想使用尚未合并到内核 RNG 中的其他熵源,则不直接使用内核 RNG 可能更有意义。例如,Haveged 可以提供使用抖动熵生成的随机数据。如果可能,额外的熵源不应取代内核 RNG,而应通过从额外的熵源和内核 RNG 中播种用户空间 CRNG 来与内核 RNG 结合使用。也可以将额外的熵写入
/dev/random
,使其合并到内核 RNG 中,但这可能需要长达 60 秒才能生效,因为内核 CRNG 重新播种计划。注意:内核已经使用 RDSEED 和其他硬件随机数生成器作为熵源。
参见
- 内核 RNG 改进
- random: 使用基于 Chacha20 的 CRNG 替换非阻塞池 - 引入了 ChaCha20 CRNG (2016)
- random: 尝试主动添加熵而不是被动等待 - 使基于 CPU 的抖动熵被用作后备方案 (2019)
- 移除 Linux /dev/random 阻塞池 - 使
/dev/random
使用 CRNG,就像/dev/urandom
一样,并且不再尝试提供“真正”的随机性 (2020) - Linux 5.17 和 5.18 的随机数生成器增强功能 (2022)
- random: 在每个 CPU 密钥上使用更简单的快速密钥擦除流程 - 简化和优化了 CRNG 设计 (2022)
- random: 不要假装处理过早的下一个安全模型 - 使新的熵被更快地使用 (2022)
- random: 在 /dev/urandom 读取时机会性地初始化 - 使
/dev/urandom
在 x86 上与/dev/random
一样安全 (2022)
- 随机性 - 一篇解释不同 RNG 的科普文章
- ENT - 一个用于测试随机序列的简单程序(熵、卡方检验、蒙特卡罗、相关性等)
- OpenSSL 随机数生成器分析 - 关于 OpenSSL 功能中 RNG 重新播种风险的论文