]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
random-util: use RDRAND for randomness if the kernel doesn't want to give us any
authorLennart Poettering <lennart@poettering.net>
Thu, 26 Jul 2018 08:42:01 +0000 (10:42 +0200)
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Tue, 21 Aug 2018 18:13:32 +0000 (20:13 +0200)
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.

src/basic/random-util.c
src/basic/random-util.h
src/test/test-random-util.c

index 91481559dbb3bcc7654e69837892ffe03908de26..aa04cc2318535ace7a64d1072da5af40c995abc7 100644 (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>
 #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;
         }
index 9a103f0e948a486397df2e5f276eb8cc024fc74f..affcc9ac1da5ddec2a8bebe297d6bcba626ea3aa 100644 (file)
@@ -21,3 +21,5 @@ static inline uint32_t random_u32(void) {
         random_bytes(&u, sizeof(u));
         return u;
 }
+
+int rdrand64(uint64_t *ret);
index a4760b54a3bd50629553d08ebf8b85623fd7cd85..70301a7782132da2db57a8c3e160d562e1f434e2 100644 (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;
 }