问题的标题和正文提出两个不同的问题:操作系统如何创建熵(这实际上应该是获取熵),以及操作系统如何从该熵产生伪随机性。我将从解释差异开始。
随机性来自何处?
随机数生成器(RNG)有两种类型:
- 伪随机数生成器(PRNG),也称为确定性随机位生成器(DRBG)或它们的组合,是确定性算法,可维护固定大小的可变内部状态并从该状态计算其输出。
- 硬件随机数生成器(HRNG),也称为“真”随机数生成器,是基于物理现象的。“真实”有点用词不当,因为没有已知真正随机的信息源,只有未知的可预测信息源。
某些应用程序(例如物理现象的模拟)可能会满足通过统计测试的随机数。其他应用程序(例如生成加密密钥)需要更强的属性:不可预测性。不可预测性是安全属性,不是(仅)统计属性:这意味着对手无法猜测随机数生成器的输出。(更准确地说,您可以通过测量对手猜测RNG输出的每一位的概率来衡量RNG的质量。如果该概率与1/2明显不同,则表明RNG不好。)
有些物理现象会产生具有良好统计特性的随机数据,例如,放射性衰变,对背景噪声的某些天文观测或股市波动。这样的物理测量需要调节(白化),以将偏差的概率分布转换为均匀的概率分布。大家都知道的物理测量方法并不适合密码学:股市波动可能对地理哈希有用,但是您不能使用它们来生成密钥。
密码学需要保密:对手必须不能发现进行调节的数据。存在密码安全的伪随机数生成器(CSPRNG):PRNG算法,其输出除了具有良好的统计特性外,还适合用于密码应用。使CSPRNG 加密安全的特性之一是,它的输出不允许对手重建内部状态(知道所有位,但CSPRNG产生的一位无助于找到丢失的位)。我不会介绍CSPRNG的制作方法,因为这很简单-您可以遵循专业密码学家提供的食谱(使用标准算法,例如NIST SP 800-90A中的 Hash_DRBG,HMAC_DRBG或CTR_DRBG)或ANSI X9.31 PRNG。为了确保安全,CSPRNG需要其状态的两个属性:
- 必须从头开始并始终将状态保密(尽管暴露状态不会透露过去的输出)。
- 状态必须是线性的:RNG绝不能从同一状态开始两次。
随机数生成器的体系结构
实际上,几乎所有好的随机数发生器都将CSPRNG与一个或多个熵源结合在一起。简而言之,熵是对数据源不可预测性的一种度量。仅基于硬件RNG的随机数生成器很难:
- 无论如何,原始物理数据可能都需要进行条件处理,以将概率数据转换为统一的分布。
- 随机源的输出必须保密。
- 与需求相比,熵源通常很慢。
因此,操作系统中的RNG 几乎总是这样工作:
- 积累足够的熵以建立不可预测的内部状态。
- 使用累积的熵作为种子,即作为内部状态的初始值,运行CSPRNG。
- 可选地,定期将其他熵混合到内部状态中。(这不是严格必要的,因为熵不会以任何可测量的速率“消耗”。它有助于防止泄漏RNG状态的某些威胁而不会损害整个系统。)
随机数生成服务是操作系统工作的一部分,因为熵收集需要访问硬件,并且熵源构成了共享资源:操作系统必须组装它们并从中获取适合应用的输出。在操作系统中需要对熵源进行伪随机调节。它也可能是加密安全的,因为从根本上讲这并不困难(并且在应用程序彼此不信任的操作系统上是必需的;在完全协作的系统上,如果操作系统需要运行,则每个应用程序必须在内部运行自己的CSPRNG仍然没有提供)。
大多数具有持久性存储的系统在启动时都会从磁盘加载RNG种子(我将使用“ disk”作为任何一种持久性存储的缩写),并使用从该种子生成的一些新的伪随机数据覆盖种子,或与从该种子生成的随机数据以及其他熵源一起提供(如果可用)。这样,即使重新启动后熵不可用,来自前一个会话的熵也会被重用。
必须注意保存状态。还记得我说过的状态必须是线性的吗?如果从相同的磁盘状态启动两次,您将获得相同的RNG输出。如果您的环境中有这种可能,则需要另一个熵源。从备份还原或克隆虚拟机时,请当心。一种克隆技术是将存储的熵与一些可预测但唯一的环境数据(例如时间和MAC地址)混合;请注意,如果环境数据是可预测的,则拥有存储的VM状态的任何人都可以重建派生的VM实例的种子。
熵源
查找(并正确使用)熵源是操作系统中随机数生成过程中最具挑战性的部分。可用的熵源将必然取决于硬件以及硬件在哪个环境中运行。
如果幸运的话,您的硬件将提供一个可用作熵源的外围设备:专用或旁侧的硬件随机数生成器。例如:
NIST SP800-90B提供了硬件RNG的设计指南。评估硬件RNG 很难。硬件RNG通常是精致的野兽,需要小心使用:许多类型的RNG在启动后需要一些时间,在两次读取之间需要一些时间才能使其不稳定,它们通常对环境条件(例如温度等)敏感。
基于Ivy Bridge架构的Intel x86-64处理器提供了RdRand
说明,该说明提供了CSPRNG的输出,该输出由热噪声引起。大多数智能手机处理器都包含硬件熵源,尽管Android并不总是使用它。
缺乏强熵源的系统必须结合弱熵源并希望(确保一个词太强)足以满足要求。随机移动鼠标在客户端计算机上很流行,并且您可能已经看到某些要求您移动鼠标的加密程序显示的安全性(即使在任何21世纪PC操作系统上,操作系统都会积累熵,而应用程序无需打扰)。
如果您想看一个例子,您可以看一下Linux,尽管要注意它并不完美。特别是,/dev/random
阻塞过于频繁(因为它阻塞直到可用的熵足够大,对熵的概念过于保守),而/dev/urandom
除了第一次启动时几乎总是好的,但是当熵不足时则没有任何指示。Linux具有许多HRNG设备的驱动程序,并累积各种设备(包括输入设备)和磁盘定时的熵。
如果您拥有(机密)持久性存储,则可以使用它来保存一次引导到下一次引导的熵,如上所述。第一次启动是一个微妙的时期:此时系统可能处于相当可预测的状态,尤其是在实质上以相同方式在工厂外运行的批量生产的设备上。在工厂中为某些具有持久性存储的嵌入式设备提供了初始种子(由工厂计算机上运行的RNG产生)。在虚拟服务器环境中,当从主机或从熵服务器实例化虚拟机时,可以设置初始熵。
播种不佳的设备在实践中是一个普遍存在的问题- 对公共RSA密钥的研究发现,许多服务器和设备的密钥都是由不良RNG(很可能是良好的PRNG)生成的,而该PRNG种子不足。作为OS设计师,您无法独自解决此问题:控制部署链是实体的工作,以确保RNG在首次启动时能正确植入种子。作为OS设计人员,您的任务是提供一个适当的RNG,包括提供第一个种子的接口,并确保在正确播种RNG之前使用RNG进行正确的错误信号发送。