今天我想:好吧,即使对RDRAND实现NIST SP 800-90A有很大的怀疑,它仍然是伪随机数发生器(PRNG)的硬件实现,对于非敏感应用程序必须足够好。所以我想在我的游戏中使用它而不是Mersenne Twister。
因此,为了查看使用该指令是否有任何性能提升,我比较了以下两个代码的时间:
// test.cpp
#include <cstdio>
int main()
{
unsigned int rnd = 0;
for(int i = 0; i < 10000000; ++i) {
__builtin_ia32_rdrand32_step(&rnd);
}
printf("%x\n", rnd);
}
和
//test2.cpp
#include <cstdio>
#include <random>
int main()
{
unsigned int rnd = 0;
__builtin_ia32_rdrand32_step(&rnd);
std::mt19937 gen(rnd);
for(int i = 0; i < 10000000; ++i) {
rnd ^= gen();
}
printf("%x\n", rnd);
}
通过运行两个我得到:
$ time ./test
d230449a
real 0m0.361s
user 0m0.358s
sys 0m0.002s
$ time ./test2
bfc4e472
real 0m0.051s
user 0m0.050s
sys 0m0.002s
因此,Mersenne Twister在我的CPU上比RDRAND快得多。好吧,我很失望,被排除在游戏之外。但RDRAND是一个加密安全的PRNG(CSPRNG),所以它在幕后做了很多......更公平的是将它与其他CSPRNG进行比较。所以我采用了我的Rabbit实现(RFC的简单翻译为C,没有花哨的性能技巧),并编写了以下测试:
// test3.cpp
#include <cstdio>
extern "C"
{
#include "rabbit.h"
}
int main()
{
rabbit_state s;
unsigned long long buf[2];
__builtin_ia32_rdrand64_step(&buf[0]);
__builtin_ia32_rdrand64_step(&buf[1]);
rabbit_init_key(&s, (uint8_t*)&buf[0]);
for(int i = 0; i < 10000000; ++i) {
rabbit_extract(&s, (uint8_t*)&buf[0]);
}
printf("%llx\n", buf[0]);
}
令我惊讶的是,产生的伪随机数据是前两个的两倍,我得到的时间比RDRAND好:
$ time ./test3
8ef9772277b70aba
real 0m0.344s
user 0m0.341s
sys 0m0.002s
这三个都是在启用优化的情况下编译的。
因此,我们普遍存在一种偏执狂,即RDRAND将NSA后门嵌入到每个人的软件加密中。此外,我们至少有一个软件CSPRNG比RDRAND快,而最广泛使用的体面PRNG,Mersenne Twister,比RDRAND快很多。最后,我们拥有开源可审计软件熵池,如/dev/random
和/dev/urandom
,它们不会隐藏在AES的双重加扰层之后,如RDRAND。
所以,问题是:人们应该使用RDRAND吗?它有合法用途吗?或者我们应该完全停止使用它吗?
答案 0 :(得分:15)
正如owlstead所指出的那样,RDRAND播种的真实随机性。特别是,它经常使用128位硬件生成的随机性重新组合其内部CSPRNG,从而保证每511 * 128位至少重置一次。请参阅本文档的第4.2.5节:
因此,在您的示例中,您使用单个128位种子从rabbit_extract生成1000万个随机抽取。在RDRAND版本中,您拥有相当于250万个128位绘图,这意味着CSPRING至少重新接种了2,500,000 / 511 = 4,892次。
因此,在您的兔子示例中,不是128位熵,而是至少有4,892 * 128 = 626,176位熵进入RDRAND示例。
在没有硬件支持的情况下,这比你在0.361秒内获得的熵要多得多。如果您正在做大量真实随机性很重要的事情,这可能很重要。一个例子是Shamir秘密共享大量数据 - 不确定是否还有其他数据。
总而言之 - 它不是为了速度,而是为了高安全性。当然,问题是它的后门是否令人不安,但你总是可以将其与其他来源混淆,至少它不会伤害你。
答案 1 :(得分:6)
RDRAND
不仅仅是PRNG。它是一种符合FIPS标准的白化TRNG。不同之处在于,您可以依赖RDRAND
来包含从CPU直接检索的大量实际熵。因此RDRAND
的主要用途是为OS /库/应用程序提供熵。
应用程序检索熵的唯一其他好方法通常是使用OS提供的熵源,例如/dev/random
或/dev/urandom
(通常从/dev/random
绘制熵)。但是,该操作系统还需要在某处找到熵。通常,磁盘和网络访问时间的微小差异用于此(+其他半随机输入)。这些设备并不总是存在,并且不是设计为熵源;它们往往不是很好的来源,也不是很快。因此,在支持它的系统上,RDRAND
通常用作操作系统的加密安全随机数生成器的熵源。
关于速度,特别是对于游戏,使用(非安全)PRNG是完全有效的。如果你想拥有一个合理的随机种子,那么用RDRAND
的结果播种它可能是一个好主意,虽然从操作系统提供的RNG播种它可能是一个更便携,甚至更安全的选择(如果你不完全信任英特尔或美国。
请注意,目前RDRAND是使用(AES)CTR_DRBG而不是为Rabbit等速度创建的(分析不太好的)流密码实现的,因此Rabbit的速度应该不足为奇。更重要的是,它每次调用时都必须检索熵。
答案 2 :(得分:3)
英特尔的RDRAND有合法用途吗?
是
考虑Monte-Carlo simulation。它没有加密需求,所以它是否由NSA后退无关紧要。
或者我们应该完全停止使用它吗?
我们无法回答这个问题。这是一个汇合用例,要求和个人偏好。
...此外,我们至少有一个软件CSPRNG比RDRAND更快,而且使用最广泛的PRNG ...&#34;
Mersenne Twister在初始化后没有Twist可能会更快,因为它从状态数组中返回一个单词。但是我怀疑它和RDRAND一样快。我知道RDRAND可以在连续流中基于总线宽度达到理论极限。
根据英特尔的大卫约翰斯顿(设计电路)的说法,这就像800 + MB / s。请参阅DJ What is the latency and throughput of the RDRAND instruction on Ivy Bridge?的答案。
因此,我们普遍存在一种偏执狂,即RDRAND将NSA后门嵌入到每个人的软件加密中。
偏执狂的人至少有两种选择。首先,他们可以放弃使用RDRAND和RDSEED。其次,他们可以使用RDRAND和RDSEED的输出来播种另一个生成器,然后使用第二个生成器的输出。我相信Linux内核采用第二种方法。
答案 3 :(得分:1)
这里有一个天体物理学研究论文(http://iopscience.iop.org/article/10.3847/1538-4357/aa7ede/meta;jsessionid=A9DA9DDB925E6522D058F3CEEC7D0B21.ip-10-40-2-120),(非支付)版本(https://arxiv.org/abs/1707.02212)给出了RdRand的合法用途。
它检查了RdRand对蒙特卡罗模拟器的影响,正如早先建议的那样。但是作者没有发现使用或不使用RdRand的结果有任何统计差异。从性能的角度来看,看起来Mersenne Twister的速度要快得多。我认为第2.2.1节和第5节都有所有细节。
答案 4 :(得分:0)
真正的随机性vs伪随机性。
rdrand是真正的熵的硬件来源,并且它是一个功能强大的熵(它也不是Intel专有的-AMD也提供相同的熵-那么为什么您会选择Intel?那么,我专注于Intel的实现,因为与Zen2 [ryzen 3xxx]相比,它们提供了更多的信息并且具有更高的吞吐量。
忘记时钟周期的吞吐量-这在这里是一种误导性指标,因为它实际上并没有关系。吞吐量受单个线程的往返延迟和运行在800 Mhz的实际硬件实现的限制,固定的8个周期,并且每个事务可以提供64位随机值。 仅查看运行代码的线程所占用的时钟周期将非常取决于当前的时钟速率-空转800 MHz似乎比以4.8 Ghz的速度快6倍。 但是内核与DRNG之间的延迟是内核间延迟的顺序,您有两次,大约80ns-结果大约为12MHz @ 8byte =>〜100 MByte / s / Thread,最大为800 MB /秒。
这样做的另一个好处是:它不会像大多数PRNG那样消耗您的CPU资源,因此,尽管其他生成器可以具有更高的吞吐量(1-2个数量级),但它们也需要更多的资源。在某些情况下,您需要大量随机数,并且在严格的计算繁琐循环中,rdrand可能会提供更好的性能,但是当然,这在很大程度上取决于确切的方案和硬件。
如果您仅需要一些随机数进行某些模拟或需要准随机数进行游戏,那么rdrand可能不是最佳选择。如果您需要真正的安全随机数?使用它-如果您担心某些后门问题,可以将其与其他熵源结合使用。