From: Lennart Poettering Date: Thu, 26 Jul 2018 08:42:01 +0000 (+0200) Subject: random-util: use RDRAND for randomness if the kernel doesn't want to give us any X-Git-Tag: v240~819 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=97fa202a61c040215b0f9460059d8d5621a5980a;p=thirdparty%2Fsystemd.git 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. --- diff --git a/src/basic/random-util.c b/src/basic/random-util.c index 91481559dbb..aa04cc23185 100644 --- a/src/basic/random-util.c +++ b/src/basic/random-util.c @@ -1,5 +1,9 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ +#ifdef __x86_64__ +#include +#endif + #include #include #include @@ -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; } diff --git a/src/basic/random-util.h b/src/basic/random-util.h index 9a103f0e948..affcc9ac1da 100644 --- a/src/basic/random-util.h +++ b/src/basic/random-util.h @@ -21,3 +21,5 @@ static inline uint32_t random_u32(void) { random_bytes(&u, sizeof(u)); return u; } + +int rdrand64(uint64_t *ret); diff --git a/src/test/test-random-util.c b/src/test/test-random-util.c index a4760b54a3b..70301a77821 100644 --- a/src/test/test-random-util.c +++ b/src/test/test-random-util.c @@ -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; }