Use getrandom() if available, or else arc4random_buf(). Only fall
back to using the built-in PRNG if those aren't found on the system.
For the sizes of data mutt typically requests, getrandom() should
never fail, but nonetheless add code to make it retry on interrupt,
and fall back to the built-in PRNG only if it has to.
FreeBSD and OpenBSD have rewritten arc4random() to use ChaCha20, which
should provide more than sufficient randomness. The call has no
return value and always succeeds, so there is no error handling
written for it.
Many thanks to Werner Koch, Greg KH, and Alejandro Colomar for their
feedback.
---
This is passing sr.ht CI, and I'm using it on my computer right now.
Note that I adjusted the length_requested type for the old PRNG code to
be size_t and tweeked the code slightly because of that.
Feedback welcome.
configure.ac | 4 ++++
mutt_random.c | 63 ++++++++++++++++++++++++++++++++++++++++++---------
mutt_random.h | 3 +--
3 files changed, 57 insertions(+), 13 deletions(-)
diff --git a/configure.ac b/configure.ac
index 5b455aff..456b916a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -423,6 +423,10 @@ AC_CHECK_TYPE(ssize_t, [],
AC_CHECK_FUNCS(fgetpos memmove memccpy setegid srand48 strerror)
+dnl random data functions
+AC_CHECK_HEADERS(sys/random.h)
+AC_CHECK_FUNCS(getrandom arc4random_buf)
+
AC_REPLACE_FUNCS([setenv strcasecmp strdup strsep strtok_r wcscasecmp])
AC_REPLACE_FUNCS([strcasestr mkdtemp])
diff --git a/mutt_random.c b/mutt_random.c
index 52ba44e5..c688e8d4 100644
--- a/mutt_random.c
+++ b/mutt_random.c
@@ -25,25 +25,38 @@
#include <fcntl.h>
#include <string.h>
-#include <sys/time.h>
-#include <sys/types.h>
-#include <unistd.h>
+#ifdef HAVE_SYS_TIME_H
+ #include <sys/time.h>
+#endif
+#ifdef HAVE_SYS_RANDOM_H
+ #include <sys/random.h>
+#endif
+#include <errno.h>
+
+/* Built-in LRFS113 PRNG code and variables.
+ *
+ * It isn't used on OpenBSD, where only arc4random_buf() is available,
+ * so undef it for that case.
+ */
+#if defined(HAVE_GETRANDOM) || !defined(HAVE_ARC4RANDOM_BUF)
static uint32_t z[4]; /* Keep state for LFRS113 PRNG */
static int rand_bytes_produced = 0;
static time_t time_last_reseed = 0;
-void mutt_random_bytes(char *random_bytes, int length_requested)
+static void prng_reseed(void);
+
+static void prng_random_bytes(char *random_bytes, size_t length_requested)
{
/* Reseed every day or after more than a 100000 random bytes produced */
if (time(NULL) - time_last_reseed > 86400 || rand_bytes_produced > 100000)
- mutt_reseed();
+ prng_reseed();
uint32_t b;
/* The loop below is our implementation of the LFRS113 PRNG algorithm by
* Pierre L'Ecuyer */
- for (int i = length_requested; i > 0;)
+ for (size_t i = length_requested; i > 0;)
{
b = ((z[0] << 6) ^ z[0]) >> 13;
z[0] = ((z[0] & 4294967294U) << 18) ^ b;
@@ -56,17 +69,17 @@ void mutt_random_bytes(char *random_bytes, int
length_requested)
b = z[0] ^ z[1] ^ z[2] ^ z[3];
- if (--i >= 0) random_bytes[i] = (b >> 24) & 0xFF;
- if (--i >= 0) random_bytes[i] = (b >> 16) & 0xFF;
- if (--i >= 0) random_bytes[i] = (b >> 8) & 0xFF;
- if (--i >= 0) random_bytes[i] = (b) & 0xFF;
+ if (i > 0) random_bytes[--i] = (b >> 24) & 0xFF;
+ if (i > 0) random_bytes[--i] = (b >> 16) & 0xFF;
+ if (i > 0) random_bytes[--i] = (b >> 8) & 0xFF;
+ if (i > 0) random_bytes[--i] = (b) & 0xFF;
}
rand_bytes_produced += length_requested;
return;
}
-void mutt_reseed(void)
+static void prng_reseed(void)
{
uint32_t t[4]; /* Temp seed values from /dev/urandom */
char computer_says_no = TRUE; /* Whether /dev/urandom was usable */
@@ -105,6 +118,34 @@ void mutt_reseed(void)
for (int i = 0; i <= 3; i++)
z[i] ^= t[i];
}
+#endif /* defined(HAVE_GETRANDOM) || !defined(HAVE_ARC4RANDOM_BUF) */
+
+/* Generate length_requested random bytes of data */
+void mutt_random_bytes(char *random_bytes, size_t length_requested)
+{
+#if defined(HAVE_GETRANDOM)
+ size_t res;
+
+ do
+ {
+ res = getrandom(random_bytes, length_requested, GRND_NONBLOCK);
+ } while ((res == (size_t) -1) && (errno == EINTR));
+
+ if ((res == (size_t) -1) || (res < length_requested))
+ {
+ if (res < length_requested)
+ {
+ length_requested -= res;
+ random_bytes += res;
+ }
+ prng_random_bytes(random_bytes, length_requested);
+ }
+#elif defined(HAVE_ARC4RANDOM_BUF)
+ arc4random_buf(random_bytes, length_requested);
+#else
+ prng_random_bytes(random_bytes, length_requested);
+#endif
+}
/* Generate and Base64 encode 96 random bits and fill the buffer
output_B64 with the result. */
diff --git a/mutt_random.h b/mutt_random.h
index ed9fdf29..0417e0d6 100644
--- a/mutt_random.h
+++ b/mutt_random.h
@@ -25,6 +25,5 @@ typedef union random64
} RANDOM64;
void mutt_base64_random96(char output_B64[static 17]);
-void mutt_random_bytes(char *random_bytes, int length_requested);
-void mutt_reseed(void);
+void mutt_random_bytes(char *random_bytes, size_t length_requested);
#endif
--
2.53.0