随机数生成
根据wikipedia:Random number generation词条的定义:
- 随机数生成器(random number generator, RNG)是用于生成数字/符号序列的计算设备或物理设备。其生成的序列应当看起来是随机的,不含任何规律性特征。
生成随机数据对某些应用至关重要。随机数据可用于生成密码学密钥(密钥可用于静态数据加密)、安全地擦除磁盘、运行软件接入点等用途。
Linux内核内置的随机数生成器可生成密码学安全的伪随机数据。其从多种来源搜集熵,用于随机数生成。熵的来源包括硬件随机数生成器、中断、CPU抖动熵(CPU-based jitterentropy),不依赖单一来源。熵将通过BLAKE2s 密码学哈希函数提取,并用作一组ChaCha20密码学随机数生成器(Cryptographic Random Number Generator, CRNG)的种子。最终的随机数据由ChaCha20密码学随机数生成器生成。内核在运行时将不断搜集熵值,并周期性地更新随机数生成器的种子。
若要在用户空间访问内核内置随机数生成器,可使用内核提供的三个接口:
- getrandom(2)系统调用
/dev/random/dev/urandom
在过去,一般认为/dev/random生成的随机数随机性比/dev/urandom的好。然而,随着内核的更新,/dev/random和/dev/urandom的行为越来越相似。甚至,在目前的x86-64系统上,它们的行为是完全相同的。由于Arch Linux只支持x86-64架构,因此可以说在Arch Linux中,两者的行为是相同的。(这是由于所有x86-64 CPU都支持RDTSC 指令,因而总是可以使用CPU抖动熵算法搜集熵值;此外,大多数x86-64 CPU还支持RDRAND指令,可用于随机数生成。)
对于其它架构(特别是不支持快速周期计数器(fast cycle counter)的架构),/dev/random与/dev/urandom仍有区别:在内核认为密码学随机数生成器完成初始化之前,/dev/random将阻塞,而/dev/urandom不阻塞。因此,在通常情况下,应用若要生成长期使用的密码学密钥,仍应当依照传统,使用/dev/random,或直接使用getrandom(2)系统调用(其默认行为与/dev/random相似)。
注意,由于/dev/random只保证生成密码学安全的随机数据(已经能满足所有现实需求),而不再保证生成“真”随机数据,从其中读取数据不再会消耗内核熵池中的熵,/proc/sys/kernel/random/entropy_avail的内容应当始终是256(ChaCha20密钥的比特长度)。因此,若看到认为256“太低”,需要手动提高该值的过时文档,不必采取相应操作。
不需要密码学安全随机数的应用程序可以使用非密码学随机数生成器,例如 random(3)。
对于需要密码学安全随机数的应用程序,在现在,使用内核内置的随机数生成器即可,一般无需使用其它方式。曾经,内核的随机数生成器生成速度较慢,且没有使用许多本可以使用的熵源。但现在,经过改进,其使用了更多的熵源,在x86-64架构上吞吐量能达到大约400 MB/s。即使在需要非常高吞吐量的场景中(例如,安全地擦除磁盘),也可以直接使用/dev/urandom。
对于如下情况,可能需要使用其它方式,而不是直接使用内核随机数生成器:
- 在某些特定情况下,应用程序需要获取密码学安全随机数,且要求非常高的吞吐量/非常低的延迟(以至于不能容忍系统调用的开销)。此时可以使用运行在用户空间密码学安全随机数生成器。要保证安全性,应当使用内核随机数生成器生成的随机数作为用户空间生成器的种子, 并考虑种子的更新问题。
- 某些应用的工作环境中已有用户空间密码学安全随机数生成器,且提供了完善的使用该生成器生成随机数的API。例如,若应用程序使用了OpenSSL,则可以使用其RAND_bytes(3)函数;若应用程序使用Java编写,则可以使用
java.security.SecureRandom。一般而言,这些API底层的用户空间密码学安全随机数生成器将自动使用内核随机数生成器生成的随机数作为种子。
- 需要使用内核未包含的额外熵源。例如,Haveged可通过抖动熵(jitterentropy)生成随机数。若可能,不应使用额外的熵源完全替代内核的随机数生成器,而应当与内核随机数生成器共同使用,为用户空间密码学安全随机数生成器提供种子。此外,也可以将高熵数据写入
/dev/random,写入的数据最终将影响内核随机数生成器(由于内核密码学安全随机数生成器的更新机制,可能需要至多60秒才能生效)。需要注意,内核默认已经使用RDSEED等其它硬件随机数源作为熵源。
- Kernel RNG improvements:
- random: replace non-blocking pool with a Chacha20-based CRNG - introduced the ChaCha20 CRNG (2016)
- random: try to actively add entropy rather than passively wait for it - made CPU-based jitterentropy be used as a fallback (2019)
-
Removing the Linux /dev/random blocking pool - made
/dev/randomuse a CRNG, just like/dev/urandom, and not try to provide "true" randomness (2020) - Random number generator enhancements for Linux 5.17 and 5.18 (2022)
- random: use simpler fast key erasure flow on per-cpu keys - simplified and optimized the CRNG design (2022)
- random: do not pretend to handle premature next security model - made new entropy be used more quickly (2022)
-
random: opportunistically initialize on /dev/urandom reads - made
/dev/urandomas safe as/dev/randomon x86 (2022)
- Randomness - A popular science article explaining different RNGs
- ENT - A simple program for testing random sequences (entropy, Chi square test, Monte Carlo, correlation, etc.)
- An Analysis of OpenSSL's Random Number Generator - Paper on RNG reseeding risks in OpenSSL functionality