random-util: use RDRAND for randomness if the kernel doesn't want to give us any

Pretty much all intel cpus have had RDRAND in a long time. While
CPU-internal RNG are widely not trusted, for seeding hash tables it's
perfectly OK to use: we don't high quality entropy in that case, hence
let's use it.

This is only hooked up with 'high_quality_required' is false. If we
require high quality entropy the kernel is the only source we should
use.
This commit is contained in:
Lennart Poettering 2018-07-26 10:42:01 +02:00 committed by Zbigniew Jędrzejewski-Szmek
parent 66c91c3a23
commit 97fa202a61
3 changed files with 79 additions and 2 deletions

View file

@ -1,5 +1,9 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#ifdef __x86_64__
#include <cpuid.h>
#endif
#include <elf.h>
#include <errno.h>
#include <fcntl.h>
@ -26,6 +30,41 @@
#include "random-util.h"
#include "time-util.h"
int rdrand64(uint64_t *ret) {
#ifdef __x86_64__
static int have_rdrand = -1;
unsigned char err;
if (have_rdrand < 0) {
uint32_t eax, ebx, ecx, edx;
/* Check if RDRAND is supported by the CPU */
if (__get_cpuid(1, &eax, &ebx, &ecx, &edx) == 0) {
have_rdrand = false;
return -EOPNOTSUPP;
}
have_rdrand = !!(ecx & (1U << 30));
}
if (have_rdrand == 0)
return -EOPNOTSUPP;
asm volatile("rdrand %0;"
"setc %1"
: "=r" (*ret),
"=qm" (err));
if (!err)
return -EAGAIN;
return 0;
#else
return -EOPNOTSUPP;
#endif
}
int acquire_random_bytes(void *p, size_t n, bool high_quality_required) {
static int have_syscall = -1;
@ -68,8 +107,26 @@ int acquire_random_bytes(void *p, size_t n, bool high_quality_required) {
* a best-effort basis. */
have_syscall = true;
if (!high_quality_required)
return -ENODATA;
if (!high_quality_required) {
uint64_t u;
size_t k;
/* Try x86-64' RDRAND intrinsic if we have it. We only use it if high quality
* randomness is not required, as we don't trust it (who does?). Note that we only do a
* single iteration of RDRAND here, even though the Intel docs suggest calling this in
* a tight loop of 10 invocatins or so. That's because we don't really care about the
* quality here. */
if (rdrand64(&u) < 0)
return -ENODATA;
k = MIN(n, sizeof(u));
memcpy(p, &u, k);
/* We only get 64bit out of RDRAND, the rest let's fill up with pseudo-random crap. */
pseudorandom_bytes((uint8_t*) p + k, n - k);
return 0;
}
} else
return -errno;
}

View file

@ -21,3 +21,5 @@ static inline uint32_t random_u32(void) {
random_bytes(&u, sizeof(u));
return u;
}
int rdrand64(uint64_t *ret);

View file

@ -34,6 +34,22 @@ static void test_pseudorandom_bytes(void) {
}
}
static void test_rdrand64(void) {
int r, i;
for (i = 0; i < 10; i++) {
uint64_t x = 0;
r = rdrand64(&x);
if (r < 0) {
log_error_errno(r, "RDRAND failed: %m");
return;
}
printf("%" PRIx64 "\n", x);
}
}
int main(int argc, char **argv) {
log_set_max_level(LOG_DEBUG);
log_parse_environment();
@ -44,5 +60,7 @@ int main(int argc, char **argv) {
test_pseudorandom_bytes();
test_rdrand64();
return 0;
}