Pet*_*ter 676 security random-number-generator
我从来没有完全明白这个。假设您用任何语言编写了一个掷骰子的小程序(仅以骰子为例)。在 600,000 次滚动之后,每个数字将被滚动大约 100,000 次,这正是我所期望的。
为什么有专门针对“真正随机性”的网站?当然,根据上面的观察,获得任何数字的机会几乎恰好是它可以选择的数字数量的 1。
我在Python 中尝试过:这是 6000 万卷的结果。最高的变化是 0.15。这不是随机的吗?
1 - 9997653 2347.0
2 - 9997789 2211.0
3 - 9996853 3147.0
4 - 10006533 -6533.0
5 - 10002774 -2774.0
6 - 9998398 1602.0
Run Code Online (Sandbox Code Playgroud)
Eri*_*ert 1394
让我们玩一些电脑扑克,只有你、我和我们都信任的服务器。服务器使用一个伪随机数生成器,它在我们玩之前用 32 位种子初始化。所以大约有 40 亿副可能的套牌。
我手上有五张牌——显然我们不是在玩德州扑克。假设这些牌是一张给我,一张给你,一张给我,一张给你,依此类推。所以我有一副牌中的第一张、第三张、第五张、第七张和第九张牌。
早些时候,我运行了 40 亿次伪随机数生成器,每个种子一次,并将为每个生成的第一张卡片写到数据库中。假设我的第一张牌是黑桃皇后。在每 52 副可能的套牌中,这仅显示一张作为第一张牌,因此我们已将可能的套牌从 40 亿张减少到大约 8000 万张左右。
假设我的第二张牌是红心三。现在我使用产生黑桃皇后的 8000 万个种子作为第一个数字运行我的 RNG 8000 万次。这需要我几秒钟。我写下产生红心三的所有套牌作为第三张牌——我手中的第二张牌。这也只是套牌的 2% 左右,所以现在我们减少到 200 万套套牌。
假设我手上的第三张牌是梅花 7。我有一个包含 200 万个种子的数据库,可以分发我的两张牌;我再运行我的 RNG 200 万次以找到产生 7 个梅花的套牌中的 2% 作为第三张牌,我们只剩下 4 万套套牌。
你看这是怎么回事。我再运行我的 RNG 40000 次以找到产生我的第四张牌的所有种子,这使我们减少到 800 副牌,然后再运行 800 次以获得产生我的第五张牌的约 20 个种子,现在我只是生成那二十副牌,我知道你有二十张可能的牌中的一张。此外,我对接下来要画的东西有一个很好的想法。
现在你明白为什么真正的随机性很重要了吗?你描述它的方式,你认为分布很重要,但分布并不是使过程随机的原因。 不可预测性使过程变得随机。
根据(由于其非建设性而现已删除)评论,至少有 0.3% 的阅读过此评论的人对我的观点感到困惑。当人们反对我没有提出的观点,或者更糟糕的是,在假设我没有提出这些观点的情况下为我确实提出的观点争论,那么我知道我需要更清楚、更仔细地解释。
单词分布似乎特别混乱,所以我想仔细地列出用法。
手头的问题是:
让我们首先考虑生成用于玩扑克的随机纸牌的完美方式。然后我们将看到其他生成套牌的技术有何不同,以及是否可以利用这种差异。
让我们首先假设我们有一个标记为 的魔术盒TRNG
。作为它的输入,我们给它一个大于或等于 1 的整数 n,作为它的输出,它给我们一个真正的随机数,它介于 1 和 n 之间,包括 1 和 n。盒子的输出是完全不可预测的(当给定一个不是 1 的数字时),并且 1 和 n 之间的任何数字都与另一个数字一样;也就是说分布是均匀的。(我们可以执行其他更高级的随机性统计检查;我忽略了这一点,因为它与我的论点没有密切关系。根据假设,TRNG 在统计上是完全随机的。)
我们从一副未洗牌的纸牌开始。我们要求盒子输入一个 1 到 52 之间的数字——也就是TRNG(52)
。无论它返回什么数字,我们都会从排序后的牌组中数出那么多张牌,然后取出那张牌。它成为洗牌后的第一张牌。然后我们要求TRNG(51)
并执行相同的操作以选择第二张卡,依此类推。
另一种看待它的方式是:有 52 个!= 52 x 51 x 50 ... x 2 x 1 可能的甲板,大约是 2 226。我们真正随机地选择了其中之一。
现在我们发牌。当我查看我的卡片时,我不知道你有什么卡片。(除了明显的事实,你没有我拥有的任何卡片。)它们可以是任何卡片,概率相等。
所以让我确保我清楚地解释了这一点。我们有每个单独的输出的均匀分布TRNG(n)
;每个人以 1/n 的概率选择 1 到 n 之间的一个数。而且,这个过程的结果是我们选择了 52 个中的一个!用1/52的概率可能甲板!所以分布在该组可能的甲板的是也均匀。
好的。
现在让我们假设我们有一个不太神奇的盒子,标记为PRNG
。在您可以使用它之前,它必须以32 位无符号数作为种子。
旁白:为什么是 32?不能用 64 或 256 或 10000 位数字作为种子吗?当然。但是 (1) 在实践中,大多数现成的 PRNG 都是用 32 位数字作为种子的,并且 (2) 如果您有 10000 位的随机性来制作种子,那么您为什么要使用 PRNG?您已经拥有 10000 位随机性的来源!
无论如何,回到 PRNG 的工作原理:播种后,您可以像使用TRNG
. 也就是说,您传递给它一个数字 n,它会返回一个介于 1 和 n 之间的数字(包括 1 和 n)。此外,该输出的分布或多或少是均匀的。也就是说,当我们要求PRNG
一个 1 到 6 之间的数字时,无论种子是什么,大约六分之一的时间我们都会得到 1、2、3、4、5 或 6。
我想多次强调这一点,因为它似乎使某些评论者感到困惑。PRNG 的分布至少在两个方面是均匀的。首先,假设我们选择任何特定的种子。我们希望序列PRNG(6), PRNG(6), PRNG(6)...
一百万次会产生 1 到 6 之间数字的均匀分布。 其次,如果我们选择一百万个不同的种子并为每个种子调用PRNG(6)
一次,我们再次希望从 1 到 6 的数字均匀分布6.这些操作中 PRNG 的一致性与我所描述的攻击无关。
这个过程被称为伪随机,因为盒子的行为实际上是完全确定的;它根据种子从 2 32 种可能的行为中选择一种。也就是说,一旦它被播种,就会PRNG(6), PRNG(6), PRNG(6), ...
产生一个均匀分布的数字序列,但该序列完全由种子决定。对于给定的调用序列,例如 PRNG(52)、PRNG(51)...等等,只有 2 32 个可能的序列。种子基本上选择我们得到哪一个。
为了生成一副牌,服务器现在生成一个种子。(怎么做?我们会回到那个点。)然后他们调用PRNG(52)
,PRNG(51)
等等来生成甲板,类似于以前。
这个系统容易受到我描述的攻击。为了攻击服务器,我们首先提前将我们自己的盒子副本设为 0,然后请求PRNG(52)
并写下来。然后我们用 1 重新播种,要求PRNG(52)
,并将其写下来,一直到 2 32 -1。
现在,使用 PRNG 生成套牌的扑克服务器必须以某种方式生成种子。他们如何做到这一点并不重要。他们可以打电话TRNG(2^32)
来获取真正随机的种子。或者他们可以将当前时间作为种子,这根本不是随机的;我和你一样知道现在几点了。我的攻击点是没关系,因为我有我的数据库。当我看到我的第一张牌时,我可以消除 98% 的可能种子。当我看到我的第二张牌时,我可以消除 98% 以上,依此类推,直到最终我可以找到一些可能的种子,并且很有可能知道你手上有什么。
现在,我想再次强调,这里的假设是,如果我们调用PRNG(6)
100 万次,我们将在大约六分之一的时间内得到每个号码。该分布(或多或少)是均匀的,如果您只关心该分布的均匀性,那很好。问题的关键在于,PRNG(6)
除了我们关心的分布之外,还有其他事情吗?答案是肯定的。我们也关心不可预测性。
看待这个问题的另一种方式是,即使分配一百万个调用PRNG(6)
可能没问题,因为 PRNG 只从 2 32 种可能的行为中进行选择,它无法生成所有可能的套牌。 它只能生成2 226 张可能的牌组中的2 32张;一小部分。所以所有牌组的分布非常糟糕。但同样,这里的根本攻击是基于我们能够从其输出的小样本中成功预测过去和未来的行为。 PRNG
让我说三四次,以确保这能被理解。这里有三个分布。首先,产生随机 32 位种子的过程的分布。这可以是完全随机的、不可预测的和统一的,并且攻击仍然有效。其次,分发一百万次调用PRNG(6)
。这可以是完全统一的,攻击仍然有效。第三,我描述的伪随机过程选择的牌组分布。这种分布极差;可能只能选择 IRL 可能套牌中的一小部分。攻击取决于PRNG基于其输出的部分知识的行为的可预测性。
旁白:此攻击要求攻击者知道或能够猜测 PRNG 使用的确切算法是什么。这是否现实是一个悬而未决的问题。但是,在设计安全系统时,即使攻击者知道程序中的所有算法,您也必须将其设计为能够抵御攻击。换句话说:安全系统中必须保密以确保系统安全的部分称为“密钥”。如果您的系统的安全性取决于您使用的秘密算法,那么您的密钥包含这些算法。这是一个极其薄弱的位置!
继续。
现在让我们假设我们有第三个标记为 的魔术盒CPRNG
。它是PRNG
. 它需要 256 位种子而不是 32 位种子。它与PRNG
种子从 2 256 种可能行为之一中选择的属性共享。和我们的其他机器一样,它有一个特性,即CPRNG(n)
在 1 和 n 之间产生均匀分布的结果的大量调用:每个发生 1/n 的时间。我们可以对其进行攻击吗?
我们最初的攻击要求我们存储 2 32 个从种子到 的映射PRNG(52)
。但是 2 256是一个更大的数字;运行CPRNG(52)
这么多次并存储结果是完全不可行的。
但是假设有其他方法可以获取价值CPRNG(52)
并从中推断出有关种子的事实?到目前为止,我们一直很愚蠢,只是强制使用所有可能的组合。我们能否查看魔术盒的内部,弄清楚它是如何工作的,并根据输出推断出有关种子的事实?
号的细节过于复杂,解释,但CPRNGs被巧妙地设计成是不可行的推断任何关于从第一输出种子有用的事实CPRNG(52)
或任何输出,子集无论多么大。
好的,现在让我们假设服务器CPRNG
用于生成牌组。它需要一个 256 位的种子。它如何选择那颗种子?如果它选择攻击者可以预测的任何值,那么攻击会突然再次变得可行。如果我们能确定这 2 256 个可能的种子中,只有 40 亿个可能被服务器选择,那么我们就可以重新开始了。我们可以再次发起这种攻击,只关注可能产生的少量种子。
因此,服务器应该努力确保 256 位数字是均匀分布的——也就是说,以 1/2 256 的概率选择每个可能的种子。基本上服务器应该调用TRNG(2^256)-1
以生成CPRNG
.
如果我可以破解服务器并查看它以查看选择了什么种子怎么办?在这种情况下,攻击者知道 CPRNG 的完整过去和未来。服务器的作者需要防范这种攻击!(当然,如果我能成功发起这次攻击,那么我可能也可以直接将钱转入我的银行账户,所以也许那不是那么有趣。重点是:种子必须是一个难以猜测的秘密,并且真正随机的 256 位数字很难猜测。)
回到我之前关于纵深防御的观点:256 位种子是这个安全系统的关键。CPRNG 的思想是只要密钥是安全的,系统就是安全的;即使关于算法的所有其他事实都是已知的,只要你能把密钥保密,对手的牌是不可预测的。
好的,所以种子应该是秘密且均匀分布的,因为如果不是,我们可以发起攻击。我们假设输出的分布CPRNG(n)
是均匀的。所有可能的牌组的分布情况如何?
您可能会说:CPRNG 输出了2 256 个可能的序列,但只有 2 226 个可能的牌组。因此,可能的序列比套牌多,所以我们很好;每个可能的 IRL 套牌现在(很有可能)在这个系统中都是可能的。这是一个很好的论点,除了……
2 226只是52!的近似值。把它分开。2 256 /52!不可能是整数,因为一方面,52!可以被 3 整除,但不能被 2 整除!由于这不是一个整数,我们现在的情况是所有套牌都有可能,但有些套牌比其他套牌更有可能。
如果这不清楚,请考虑数字较小的情况。假设我们有三张牌,A、B 和 C。假设我们使用带有 8 位种子的 PRNG,因此有 256 个可能的种子。有 256 种可能的输出PRNG(3)
取决于种子;没有办法让其中的三分之一是 A,三分之一是 B,三分之一是 C,因为 256 不能被 3 整除。必须对其中一个有一点偏见。
类似地, 52 不会均匀地分成 2 256,因此必须有一些偏向于某些卡片作为第一张选择的卡片而偏向于远离其他卡片。
在我们使用 32 位种子的原始系统中,存在巨大的偏差,并且从未生产出绝大多数可能的套牌。在这个系统中,所有的甲板都可以生产,但甲板的分布仍然存在缺陷。一些套牌比其他套牌的可能性略高。
现在的问题是:我们是否有基于此缺陷的攻击?答案是在实践中,可能不是。CPRNG 被设计成如果种子真的是随机的,那么在计算上就无法区分CPRNG
和TRNG
。
好的,让我们总结一下。
伪随机数和真随机数有何不同?
它们表现出的可预测性水平不同。
为什么差异很重要?
因为在某些应用程序中,系统的安全性依赖于不可预测性。
差异是否与PRNG的输出分布有关?
分布或均匀性上缺乏单个呼叫到RNG(n)
不相关的我所描述的攻击。
正如我们所看到的,aPRNG
和a 都CPRNG
产生了从所有可能的套牌中选择任何一副套牌的概率分布很差。这PRNG
是相当糟糕的,但两者都有问题。
还有一个问题:
如果 TRNG 比 CPRNG 好得多,而 CPRNG 又比 PRNG 好得多,为什么有人使用 CPRNG 或 PRNG?
两个原因。
第一:费用。TRNG很贵。生成真正的随机数很困难。CPRNG 可以为任意多次调用提供良好的结果,其中只有一次调用 TRNG 来获取种子。不利的一面当然是您必须对该种子保密。
第二:有时我们想要可预测性,而我们所关心的只是良好的分布。如果您正在生成“随机”数据作为测试套件的程序输入,并且它显示了一个错误,那么再次运行测试套件会再次产生该错误会很好!
我希望现在更清楚了。
最后,如果你喜欢这个,那么你可能会喜欢一些关于随机性和排列主题的进一步阅读:
RNG(n)
?Bru*_*ett 166
正如 Eric Lippert 所说,它不仅仅是分发。还有其他方法可以衡量随机性。
早期的随机数生成器之一在最低有效位中有一个序列 - 它交替使用 0 和 1。因此,LSB 是 100% 可预测的。但你需要担心的不止这些。每个位都必须是不可预测的。
这是思考问题的好方法。假设您正在生成 64 位随机性。对于每个结果,取前 32 位 (A) 和后 32 位 (B),并在数组 x[A,B] 中建立索引。现在执行测试一百万次,对于每个结果,在该数字处增加数组,即 X[A,B]++;
现在绘制一个二维图,其中数字越大,该位置的像素越亮。
如果真的是随机的,颜色应该是统一的灰色。但你可能会得到模式。以 Windows NT 系统的 TCP 序列号中的“随机性”图为例:
甚至是来自 Windows 98 的这个:
这是 Cisco 路由器 (IOS) 实施的随机性。
这些图表由Micha 提供?Zalewski 的论文。在这种特殊情况下,如果可以预测某个系统的 TCP 序列号是多少,就可以在与另一个系统建立连接时冒充该系统——这将允许劫持连接、拦截通信等。即使我们无法 100% 地预测下一个数字,如果我们可以在我们的控制下创建一个新的连接,我们可以增加成功的机会。当计算机可以在几秒钟内生成 100,000 个连接时,成功攻击的几率从天文数字变为可能甚至可能。
bwD*_*aco 94
虽然计算机生成的伪随机数对于计算机用户遇到的大多数用例是可以接受的,但有些场景需要完全不可预测的随机数。
在诸如加密之类的安全敏感应用程序中,伪随机数生成器 (PRNG) 可能会产生一些值,这些值虽然看起来是随机的,但实际上可以被攻击者预测。如果使用了 PRNG 并且攻击者拥有有关 PRNG 状态的信息,那么试图破解加密系统的人可能能够猜出加密密钥。因此,对于此类应用程序,需要产生真正不可猜测的值的随机数生成器。请注意,某些 PRNG 被设计为加密安全,可用于此类安全敏感应用程序。
关于 RNG 攻击的更多信息可以在这篇维基百科文章中找到。
小智 78
我在 Python 中尝试过:这是 6000 万卷的结果。最高的变化是 0.15。这不是随机的吗?
实际上,它是如此“好”,它很糟糕......所有现有的答案都集中在给定一小部分初始值的可预测性上。我想提出另一个问题:
您的分布的标准偏差比随机滚动的标准偏差小得多
真正的随机性并没有那么接近于平均“几乎正好是它可以选择的数字数量的 1”,您将其用作质量的指示。
如果您查看有关多次掷骰子的概率分布的 Stack Exchange 问题,您将看到 N 个掷骰子的标准偏差的公式(假设真正随机的结果):
sqrt(N * 35.0 / 12.0).
Run Code Online (Sandbox Code Playgroud)
使用该公式,标准偏差为:
如果我们查看您的结果:
您不能期望有限样本的标准偏差与公式完全匹配,但它应该非常接近。然而,在 100 万卷时,您的标准差不到一半,而 6000 万卷时,您不到三分之一——情况越来越糟,这并非巧合......
伪 RNG 倾向于通过一系列不同的数字,从种子开始,而不是在特定时期内重新访问原始数字。例如,旧的 C 库rand()
函数的实现通常有 2^32 的周期,并且在重复种子之前,它们将访问 0 到 2^32-1 之间的每个数字恰好一次。因此,如果您模拟 2^32 个骰子,则前模数(%
) 结果将包括从 0 到 2^32 的每个数字,每个 1-6 结果的计数将是 715827883 或 715827882(2^32 不是 6 的倍数),因此标准偏差仅略高于 0。使用上面的公式,2^32 卷的正确标准偏差是 111924。无论如何,随着伪随机卷数的增加,您会收敛到 0 标准偏差。当滚动次数占周期的很大一部分时,这个问题可能会很严重,但一些伪 RNG 可能会出现比其他伪 RNG 更严重的问题——或者即使样本更少的问题。
因此,即使您不关心加密漏洞,在某些应用程序中,您也可能关心拥有没有过度、人为均匀结果的发行版。某些类型的模拟非常特别地试图计算出不均匀结果的后果,这些结果自然发生在大量单独随机结果的样本中,但它们在某些 pRNG 的结果中代表性不足。如果您试图模拟大量人口对某些事件的反应,这个问题可能会从根本上改变您的结果,从而导致非常不准确的结论。
举一个具体的例子:假设一个数学家告诉一个扑克机程序员,在 6000 万次模拟掷骰之后 - 用于在屏幕周围闪烁数百个小“灯”,如果有 10,013,229 或更多的 6,这是数学家期望的1 stddev 远离平均值,应该有一个小的支出。根据68–95–99.7 规则(维基百科),这应该发生在大约16%的时间(约 68% 落在标准偏差内/只有一半在标准偏差范围内)。使用您的随机数生成器,这与平均值高出约 3.5 个标准差:低于0.025% 的机会 - 几乎没有客户获得此好处。参见刚才提到的页面上的高偏差表,具体来说:
| Range | In range | Outside range | Approx. freq. for daily event |
| µ ± 1? | 0.68268... | 1 in 3 | Twice a week |
| µ ± 3.5? | 0.99953... | 1 in 2149 | Every six years |
Run Code Online (Sandbox Code Playgroud)
Chr*_*lor 51
我只是写了这个随机数生成器来生成骰子
def get_generator():
next = 1
def generator():
next += 1
if next > 6:
next = 1
return next
return generator
Run Code Online (Sandbox Code Playgroud)
你像这样使用它
>> generator = get_generator()
>> generator()
1
>> generator()
2
>> generator()
3
>> generator()
4
>> generator()
5
>> generator()
6
>> generator()
1
Run Code Online (Sandbox Code Playgroud)
等等等等。您愿意将此生成器用于运行骰子游戏的程序吗?请记住,它的分布正是您对“真正随机”生成器的期望!
伪随机数生成器基本上做同样的事情——它们生成具有正确分布的可预测数字。它们不好的原因与上面的简单随机数生成器不好的原因相同——它们不适合需要真正不可预测性的情况,而不仅仅是正确的分布。
Ale*_*zie 27
您的计算机可以执行的随机数生成适合大多数需求,您不太可能遇到需要真正随机数的时候。
不过,真正的随机数生成有其目的。在计算机安全、赌博、大型统计抽样等方面。
如果您对随机数的应用感兴趣,请查看维基百科文章。
Pra*_*bhu 27
大多数编程语言中的典型函数生成的随机数并不是纯粹的随机数。它们是伪随机数。由于它们不是纯粹的随机数,因此可以使用有关先前生成的数字的足够信息来猜测它们。所以这将是密码学安全的灾难。
例如,下面使用的随机数生成器函数glibc
不会生成纯随机数。由此产生的伪随机数是可以猜到的。这是安全问题的一个错误。历史上有过这样的灾难。这不应该用于密码学。
glibc random():
r[i] ? ( r[i-3] + r[i-31] ) % (2^32)
output r[i] >> 1
Run Code Online (Sandbox Code Playgroud)
即使在统计上非常重要,这种类型的伪随机数生成器也不应该在安全敏感的地方使用。
对伪随机密钥的著名攻击之一是对802.11b WEP 的攻击。WEP 有 104 位的长期密钥,与 24 位的 IV(计数器)连接形成 128 位的密钥,然后应用到RC4 算法生成伪随机密钥。
( RC4( IV + Key ) ) XOR (message)
Run Code Online (Sandbox Code Playgroud)
这些钥匙彼此密切相关。在这里,每一步只有 IV 增加 1,其他所有步骤保持不变。由于这不是纯随机的,它是灾难性的,很容易崩溃。通过分析大约40000帧可以恢复密钥,这是几分钟的事情。如果 WEP 使用纯粹随机的 24 位 IV,那么在大约 2^24(近 1680 万)帧之前它可能是安全的。
因此,在可能的情况下,应该在安全敏感问题中使用纯随机数生成器。
小智 13
不同之处在于伪随机生成的数字在一段时间后是可预测的(重复),而真正的随机数不是。重复所需的长度取决于用于其生成的种子的长度。
这是关于该主题的非常不错的视频:http : //www.youtube.com/watch?v=itaMNuWLzJo
小智 11
假设一个伪随机数在生成之前可以被任何人猜到。
对于琐碎的应用程序,伪随机性很好,就像你的例子一样,你会得到大约正确的百分比(大约总结果集的 1/6),但有一些细微的变化(如果你掷骰子 600k,你会看到次);
但是,当涉及到计算机安全等问题时;需要真正的随机性。
例如,RSA 算法从计算机选择两个随机数(P 和 Q)开始,然后对这些数字执行几个步骤以生成称为公钥和私钥的特殊数字。(私钥的重要部分是它是私有的,其他人都不知道!)
如果攻击者可以知道您的计算机将选择的两个“随机”数字是什么,他们可以执行相同的步骤来计算您的私钥(其他人不应该知道的!)
使用您的私钥,攻击者可以执行以下操作:a) 冒充您与您的银行交谈,b) 监听您的“安全”互联网流量并能够对其进行解码,c) 在您与互联网上的其他方之间进行伪装。
这就是真正的随机性(即无法猜测/计算)的地方。
gna*_*729 11
我用过的第一个随机数具有任意两个连续随机数的优良特性,第二个更大,概率为 0.6。不是 0.5。第三个比第二个大,概率为 0.6,依此类推。您可以想象这会对模拟造成怎样的破坏。
有些人不相信我,这甚至可以在随机数均匀分布的情况下实现,但是如果您查看序列 (1, 3, 5, 2, 4, 1, 3, 5, 2, 4, ... ) 其中两个数字中的第二个较大,概率为 0.6。
另一方面,对于模拟而言,能够重现随机数可能很重要。假设您进行了交通模拟,并想了解您可能采取的某些行动如何改善交通状况。在这种情况下,您希望能够通过尝试改善交通状况的不同操作重新创建完全相同的交通数据(例如人们试图进入城镇)。
简短的回答是,人们通常出于一个糟糕的原因需要“真正的随机性”,即他们不了解密码学。
一旦输入了一些不可预测的比特,诸如流密码和CSPRNG 之类的加密原语用于生成大量不可预测的比特流。
细心的读者现在会意识到这里有一个引导问题:我们必须收集一些熵来开始这一切。然后可以将它们提供给CSPRNG,它反过来会很高兴地提供我们需要的所有不可预测的位。因此需要硬件 RNG 来播种 CSPRNG。这是真正需要熵的唯一情况。
(我认为这应该发布在安全或密码学中。)
编辑:最后,必须选择一个随机数生成器,它对于预想的任务来说足够好,就随机数生成而言,硬件并不一定等同于好的。就像糟糕的 PRNG 一样,硬件随机源通常也有偏差。
编辑:这里的一些人假设一个威胁模型,其中攻击者可以读取 CSPRNG 的内部状态,然后得出结论,即 CSPRNG 不是安全的解决方案。这是一个糟糕的线程建模示例。如果攻击者拥有您的系统,那么游戏就结束了,简单明了。此时使用 TRNG 还是 CSPRNG 没有任何区别。
编辑:所以,总结所有这些......需要熵来播种 CSPRNG。完成此操作后,CSPRNG 将提供我们安全应用程序所需的所有不可预测位,这比我们(通常)收集熵的速度要快得多。如果不需要不可预测性,例如模拟,Mersenne Twister 将以更高的速率提供具有良好统计特性的数字。
编辑:任何愿意了解安全随机数生成问题的人都应该阅读:http : //www.cigital.com/whitepapers/dl/The_Importance_of_Reliable_Randomness.pdf
并非所有 PRNG 都适合所有用途。例如,Java.util.SecureRandom 使用 SHA1 哈希,其输出大小为 160 位。这意味着有 2 160 个可能的随机数流可以来自它。就那么简单。您不能获得超过 2 160 个内部状态值。因此 ,无论您的种子来自何处,您都无法从单个种子中获得超过2160 个独特的随机数流。Windows CryptGenRandom 被认为使用 40 字节的状态,它有 2 320 个可能的随机数流。
对标准 52 张牌进行洗牌的方法数为 52!,大约为 2 226。因此,无论种子如何,您都不能使用 Java.util.SecureRandom 来洗牌。它无法产生大约 2 66 种可能的洗牌。当然,我们不知道他们是哪个......
因此,如果我有一个 256 位的真随机源(例如,来自 Quantis RNG 卡),我可以用该种子为 CryptGenRandom() 之类的 PRNG 播种,然后使用 PRNG 洗牌牌。如果我在每次洗牌时都以真正的随机性重新播种,那就没问题了:不可预测且统计上是随机的。如果我用 Java.util.SecureRandom 做同样的事情,就会有不可能产生的 shuffle,因为它不能用 256 位的熵做种子,而且它的内部状态不能代表所有可能的 shuffle。
请注意, java.util.SecureRandom 结果既不可预测又统计随机。没有任何统计测试会发现问题!但是 RNG 的输出不足以覆盖模拟一副纸牌所需的所有可能输出的完整域。
记住,如果你把小丑加进去,就是 54 个!您必须涵盖,这需要大约 2 238 种可能性。