Skip to content
Permalink
Browse files

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.
  • Loading branch information...
poettering authored and keszybz committed Jul 26, 2018
1 parent 66c91c3 commit 97fa202a61c040215b0f9460059d8d5621a5980a
Showing with 79 additions and 2 deletions.
  1. +59 −2 src/basic/random-util.c
  2. +2 −0 src/basic/random-util.h
  3. +18 −0 src/test/test-random-util.c
@@ -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;
}
@@ -21,3 +21,5 @@ static inline uint32_t random_u32(void) {
random_bytes(&u, sizeof(u));
return u;
}

int rdrand64(uint64_t *ret);
@@ -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;
}

0 comments on commit 97fa202

Please sign in to comment.
You can’t perform that action at this time.