Comment 5 for bug 1084378

Revision history for this message
Nathan Dorfman (ndorf) wrote :

Hello. Sorry to be the bearer of bad news, but this implementation is broken in a subtle but important way:

When adding the data returned from rdrand to the system entropy pool, using the RNDADDENTROPY ioctl on /dev/random, it sets the entropy count equal to the buffer size. In other words, claiming that its data is 100% entropy.

This is clearly wrong, as the rdrand instruction only gives access to a deterministic pseudo-random number generator. It's seeded by a built-in hardware source of true entropy, but the rdrand instruction doesn't give access to the output of the entropy source, only to the output of PRNG.

The PRNG is reseeded often from this entropy source, so the output contains plenty of entropy, but nowhere near 100% (or even 1%). Intel states that you are guaranteed a reseed if you invoke the 64-bit rdrand instruction more than 1022 times (http://software.intel.com/en-us/articles/intel-digital-random-number-generator-drng-software-implementation-guide). A seed value is 256 bits. So in the worst case, we are providing 256 bits of entropy per almost 8KB of rdrand output. The problem is, when we pass it to the kernel, we claim that we provided 8KB of entropy:

(from rngd_linux.c):
    entropy.ent_count = size * 8;
    entropy.size = size;

The solution would be simple: per each ioctl(), make sure we've called rdrand64 more than 1022 times, and set the ent_count to 256.

This can actually be done using command line options in the "official" (git://git.kernel.org/pub/scm/utils/kernel/rng-tools/rng-tools.git) rng-tools, but not the version in this Ubuntu package. They have a command line option -H that lets you specify the "entropy per bit" as a floating point, which we are missing. In conjunction with the random-step option -s, the correct behavior can be coaxed out of it by using -H 0.0039062500 -s 8192. Pretty klugy, though.

I also found GPL code (I don't know the author) at https://github.com/bjencks/rngd-rdrand which cleanly and correctly does the right thing. It only supports rgrand, no other sources, so the whole thing is about 100 lines of C. It isn't productionized (doesn't fork into the background, doesn't have an init.d script, etc.) but I'm using it on my system almost as-is, and it is still fast enough to generate 2048-bit GPG key pairs nearly instantaneously on my system. The only change I made was to bump the number of rdrands per ioctl to 1024 from 1022, as the Intel docs linked above say *more* than 1022, not just 1022.