耗尽 std::random_device 的熵

Draining the entropy of std::random_device

我正在使用 std::random_device 并想检查它的剩余熵。根据cppreference.com:

std::random_device::entropy

double entropy() const noexcept;

[...]

Return value

The value of the device entropy, or zero if not applicable.

Notes

This function is not fully implemented in some standard libraries. For example, LLVM libc++ always returns zero even though the device is non-deterministic. In comparison, Microsoft Visual C++ implementation always returns 32, and boost.random returns 10.

The entropy of the Linux kernel device /dev/urandom may be obtained using ioctl RNDGETENTCNT - that's what std::random_device::entropy() in GNU libstdc++ uses as of version 8.1

所以在 Linux ang g++ >= 8.1 下,我应该很好...但我不是:

#include <random>
#include <iostream>

void drain_entropy(std::random_device& rd, std::size_t count = 1)
{
    while (count --> 0) {
        volatile const int discard = rd();
        (void) discard;
    }
}

int main()
{
    std::random_device rd;
    std::cout << "Entropy: " << rd.entropy() << '\n'; // Entropy: 32
    drain_entropy(rd, 1'000'000);
    std::cout << "Entropy: " << rd.entropy() << '\n'; // Entropy: 32
}

Live demo on Coliru (which runs under Linux, right?)

我期望从设备生成数字会耗尽它的熵。但事实并非如此。

发生了什么事?

库不会 return 熵值大于其结果类型中的位数,在本例中为 32。

libstd code:

const int max = sizeof(result_type) * __CHAR_BIT__;
if (ent > max)
  ent = max;

您链接到的文档解释了这一点:

Obtains an estimate of the random number device entropy, which is a floating-point value between 0 and log 2(max()+1) (which is equal to std::numeric_limits::digits).

您可以看到 .entropy() 是如何实现的 here

基本上,entropy() 调用 ioctl(fd, RNDGETENTCNT, &ent) 和 returns ent(在根据需要限制目标类型中的最大位数之后)。

碰巧在您 drain_entropy 调用后它没有改变。

您可以手动实现这个方法,看看它的行为是一样的。 即使你移除钳位,熵也几乎没有受到影响(它甚至可能增加)。