今天我想:好吧,即使对NIST SP 800-90A的 RDRAND 实现有很大的怀疑,它仍然是伪随机数生成器 (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 吗?它有什么合法用途吗?还是我们应该完全停止使用它?