Description
See
Lines 33 to 42 in 30308ae
if rdrand_step(&mut el) == 1 {
// AMD CPUs from families 14h to 16h (pre Ryzen) sometimes fail to
// set CF on bogus random data, so we check these values explicitly.
// See https://github.com/systemd/systemd/issues/11810#issuecomment-489727505
// We perform this check regardless of target to guard against
// any implementation that incorrectly fails to set CF.
if el != 0 && el != !0 {
return Ok(el.to_ne_bytes());
}
// Keep looping in case this was a false positive.
}
The condition if el != 0 && el != !0
is testing that the value returned by RDRAND, after it reports success, is not zero or all-one bits, i.e. never usize::MIN
or usize::MAX
. Consequently, getrandom::getrandom
will never return a result where there a single word is zero or all-one bits, where as word is a 4-byte chunk on 32-bit x86 or a 8-byte chunk on 64-bit x86, when the RDRAND implementation is being used. Such values are expected to occur every 2/2^N words on average. As a result, any use of getrandom::getrandom
returns results that are wrongly biased; 2/2^N values are rejected on an N-bit platform.
32-bit x86 support for the RDRAND feature was added in PR #134, after PR #48. The analysis on the bias when the code was implemented was based on the probability 2/2^64 which is correct when this code runs on a 64-bit CPU, but not for when it runs on a 32-bit CPU. I suspect that when PR #134 was under consideration, the difference in the bias was perhaps overlooked. The bias is notably worse on 32-bit platforms since 2/2^32 is much more likely than 2/2^64.
Outside of cryptography, I find the present solution particularly unfortunate because if getrandom's output is used for randomized testing of a 64-bit/32-bit function on x86_64/x86 on a target for which the RDRAND implementation is used, the important boundary conditions of the values usize::MIN
and usize::MAX
will never be reached, ever!
In the context of implementing cryptographic functions, and especially keygen and nonce generation, I suspect any user of this crate is likely to get pestered about this bias and will have to address it in some way.
In any case, I think the present solution is weird enough that it is worth having more eyes on it and more discussion, and also I hope we could find a better solution.
Incidentally, BoringSSL's code has this to say:
// Also disable for family 0x17, models 0x70–0x7f, due to possible RDRAND
// failures there too.
It seems like Google has reason to believe that some family 0x17 models may also be bad. So the comment indicating the problem is only with families 14h-16h may be worth updating, at least.