#include "missing_syscall.h"
#include "parse-util.h"
#include "random-util.h"
-#include "siphash24.h"
+#include "sha256.h"
#include "time-util.h"
-static bool srand_called = false;
+/* This is a "best effort" kind of thing, but has no real security value.
+ * So, this should only be used by random_bytes(), which is not meant for
+ * crypto. This could be made better, but we're *not* trying to roll a
+ * userspace prng here, or even have forward secrecy, but rather just do
+ * the shortest thing that is at least better than libc rand(). */
+static void fallback_random_bytes(void *p, size_t n) {
+ static thread_local uint64_t fallback_counter = 0;
+ struct {
+ char label[32];
+ uint64_t call_id, block_id;
+ usec_t stamp_mono, stamp_real;
+ pid_t pid, tid;
+ uint8_t auxval[16];
+ } state = {
+ /* Arbitrary domain separation to prevent other usage of AT_RANDOM from clashing. */
+ .label = "systemd fallback random bytes v1",
+ .call_id = fallback_counter++,
+ .stamp_mono = now(CLOCK_MONOTONIC),
+ .stamp_real = now(CLOCK_REALTIME),
+ .pid = getpid(),
+ .tid = gettid()
+ };
-int genuine_random_bytes(void *p, size_t n, RandomFlags flags) {
- static int have_syscall = -1;
- _cleanup_close_ int fd = -1;
-
- /* Gathers some high-quality randomness from the kernel. This call won't block, unless the RANDOM_BLOCK
- * flag is set. If it doesn't block, it will still always return some data from the kernel, regardless
- * of whether the random pool is fully initialized or not. When creating cryptographic key material you
- * should always use RANDOM_BLOCK. */
+#if HAVE_SYS_AUXV_H
+ memcpy(state.auxval, ULONG_TO_PTR(getauxval(AT_RANDOM)), sizeof(state.auxval));
+#endif
- if (n == 0)
- return 0;
+ while (n > 0) {
+ struct sha256_ctx ctx;
- /* Use the getrandom() syscall unless we know we don't have it. */
- if (have_syscall != 0 && !HAS_FEATURE_MEMORY_SANITIZER) {
- for (;;) {
- ssize_t l = getrandom(p, n, FLAGS_SET(flags, RANDOM_BLOCK) ? 0 : GRND_INSECURE);
-
- if (l > 0) {
- have_syscall = true;
-
- if ((size_t) l == n)
- return 0; /* Yay, success! */
-
- /* We didn't get enough data, so try again */
- assert((size_t) l < n);
- p = (uint8_t*) p + l;
- n -= l;
- continue;
-
- } else if (l == 0) {
- have_syscall = true;
- return -EIO;
-
- } else if (ERRNO_IS_NOT_SUPPORTED(errno)) {
- /* We lack the syscall, continue with reading from /dev/urandom. */
- have_syscall = false;
- break;
-
- } else if (errno == EINVAL) {
- /* If we previously passed GRND_INSECURE, and this flag isn't known, then
- * we're likely running an old kernel which has getrandom() but not
- * GRND_INSECURE. In this case, fall back to /dev/urandom. */
- if (!FLAGS_SET(flags, RANDOM_BLOCK))
- break;
-
- return -errno;
- } else
- return -errno;
+ sha256_init_ctx(&ctx);
+ sha256_process_bytes(&state, sizeof(state), &ctx);
+ if (n < SHA256_DIGEST_SIZE) {
+ uint8_t partial[SHA256_DIGEST_SIZE];
+ sha256_finish_ctx(&ctx, partial);
+ memcpy(p, partial, n);
+ break;
}
+ sha256_finish_ctx(&ctx, p);
+ p = (uint8_t *) p + SHA256_DIGEST_SIZE;
+ n -= SHA256_DIGEST_SIZE;
+ ++state.block_id;
}
-
- fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY);
- if (fd < 0)
- return errno == ENOENT ? -ENOSYS : -errno;
-
- return loop_read_exact(fd, p, n, true);
}
-static void clear_srand_initialization(void) {
- srand_called = false;
-}
-
-void initialize_srand(void) {
- static bool pthread_atfork_registered = false;
- unsigned x;
+void random_bytes(void *p, size_t n) {
+ static bool have_getrandom = true, have_grndinsecure = true;
+ _cleanup_close_ int fd = -1;
- if (srand_called)
+ if (n == 0)
return;
-#if HAVE_SYS_AUXV_H
- /* The kernel provides us with 16 bytes of entropy in auxv, so let's try to make use of that to seed
- * the pseudo-random generator. It's better than nothing... But let's first hash it to make it harder
- * to recover the original value by watching any pseudo-random bits we generate. After all the
- * AT_RANDOM data might be used by other stuff too (in particular: ASLR), and we probably shouldn't
- * leak the seed for that. */
-
- const void *auxv = ULONG_TO_PTR(getauxval(AT_RANDOM));
- if (auxv) {
- static const uint8_t auxval_hash_key[16] = {
- 0x92, 0x6e, 0xfe, 0x1b, 0xcf, 0x00, 0x52, 0x9c, 0xcc, 0x42, 0xcf, 0xdc, 0x94, 0x1f, 0x81, 0x0f
- };
-
- x = (unsigned) siphash24(auxv, 16, auxval_hash_key);
- } else
-#endif
- x = 0;
-
- x ^= (unsigned) now(CLOCK_REALTIME);
- x ^= (unsigned) gettid();
-
- srand(x);
- srand_called = true;
-
- if (!pthread_atfork_registered) {
- (void) pthread_atfork(NULL, NULL, clear_srand_initialization);
- pthread_atfork_registered = true;
+ for (;;) {
+ ssize_t l;
+
+ if (!have_getrandom)
+ break;
+
+ l = getrandom(p, n, have_grndinsecure ? GRND_INSECURE : GRND_NONBLOCK);
+ if (l > 0) {
+ if ((size_t) l == n)
+ return; /* Done reading, success. */
+ p = (uint8_t *) p + l;
+ n -= l;
+ continue; /* Interrupted by a signal; keep going. */
+ } else if (l == 0)
+ break; /* Weird, so fallback to /dev/urandom. */
+ else if (ERRNO_IS_NOT_SUPPORTED(errno)) {
+ have_getrandom = false;
+ break; /* No syscall, so fallback to /dev/urandom. */
+ } else if (errno == EINVAL && have_grndinsecure) {
+ have_grndinsecure = false;
+ continue; /* No GRND_INSECURE; fallback to GRND_NONBLOCK. */
+ } else if (errno == EAGAIN && !have_grndinsecure)
+ break; /* Will block, but no GRND_INSECURE, so fallback to /dev/urandom. */
+
+ break; /* Unexpected, so just give up and fallback to /dev/urandom. */
}
-}
-/* INT_MAX gives us only 31 bits, so use 24 out of that. */
-#if RAND_MAX >= INT_MAX
-assert_cc(RAND_MAX >= 16777215);
-# define RAND_STEP 3
-#else
-/* SHORT_INT_MAX or lower gives at most 15 bits, we just use 8 out of that. */
-assert_cc(RAND_MAX >= 255);
-# define RAND_STEP 1
-#endif
-
-void pseudo_random_bytes(void *p, size_t n) {
- uint8_t *q;
-
- /* This returns pseudo-random data using libc's rand() function. You probably never want to call this
- * directly, because why would you use this if you can get better stuff cheaply? Use random_bytes()
- * instead, see below: it will fall back to this function if there's nothing better to get, but only
- * then. */
+ fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY);
+ if (fd >= 0 && loop_read_exact(fd, p, n, false) == 0)
+ return;
- initialize_srand();
+ /* This is a terrible fallback. Oh well. */
+ fallback_random_bytes(p, n);
+}
- for (q = p; q < (uint8_t*) p + n; q += RAND_STEP) {
- unsigned rr;
+int crypto_random_bytes(void *p, size_t n) {
+ static bool have_getrandom = true, seen_initialized = false;
+ _cleanup_close_ int fd = -1;
- rr = (unsigned) rand();
+ if (n == 0)
+ return 0;
-#if RAND_STEP >= 3
- if ((size_t) (q - (uint8_t*) p + 2) < n)
- q[2] = rr >> 16;
-#endif
-#if RAND_STEP >= 2
- if ((size_t) (q - (uint8_t*) p + 1) < n)
- q[1] = rr >> 8;
-#endif
- q[0] = rr;
+ for (;;) {
+ ssize_t l;
+
+ if (!have_getrandom)
+ break;
+
+ l = getrandom(p, n, 0);
+ if (l > 0) {
+ if ((size_t) l == n)
+ return 0; /* Done reading, success. */
+ p = (uint8_t *) p + l;
+ n -= l;
+ continue; /* Interrupted by a signal; keep going. */
+ } else if (l == 0)
+ return -EIO; /* Weird, should never happen. */
+ else if (ERRNO_IS_NOT_SUPPORTED(errno)) {
+ have_getrandom = false;
+ break; /* No syscall, so fallback to /dev/urandom. */
+ }
+ return -errno;
}
-}
-void random_bytes(void *p, size_t n) {
+ if (!seen_initialized) {
+ _cleanup_close_ int ready_fd = -1;
+ int r;
- /* This returns high quality randomness if we can get it cheaply. If we can't because for some reason
- * it is not available we'll try some crappy fallbacks.
- *
- * What this function will do:
- *
- * • Use getrandom(GRND_INSECURE) or /dev/urandom, to return high-quality random values if
- * they are cheaply available, or less high-quality random values if they are not.
- *
- * • This function will return pseudo-random data, generated via libc rand() if nothing
- * better is available.
- *
- * • This function will work fine in early boot
- *
- * • This function will always succeed
- *
- * What this function won't do:
- *
- * • This function will never fail: it will give you randomness no matter what. It might not
- * be high quality, but it will return some, possibly generated via libc's rand() call.
- *
- * • This function will never block: if the only way to get good randomness is a blocking,
- * synchronous getrandom() we'll instead provide you with pseudo-random data.
- *
- * This function is hence great for things like seeding hash tables, generating random numeric UNIX
- * user IDs (that are checked for collisions before use) and such.
- *
- * This function is hence not useful for generating UUIDs or cryptographic key material.
- */
-
- if (genuine_random_bytes(p, n, 0) >= 0)
- return;
+ ready_fd = open("/dev/random", O_RDONLY|O_CLOEXEC|O_NOCTTY);
+ if (ready_fd < 0)
+ return -errno;
+ r = fd_wait_for_event(ready_fd, POLLIN, USEC_INFINITY);
+ if (r < 0)
+ return r;
+ seen_initialized = true;
+ }
- /* If for some reason some user made /dev/urandom unavailable to us, or the kernel has no entropy, use a PRNG instead. */
- pseudo_random_bytes(p, n);
+ fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY);
+ if (fd < 0)
+ return -errno;
+ return loop_read_exact(fd, p, n, false);
}
size_t random_pool_size(void) {