When mutt needs a random number (for example for naming the tmp files or generating a boundary for MIME attachments) it currently uses different implementation specific functions: random() and either lrand48() or rand() are used. Which one gets used in specific and the quality of its randomness is dependant on the code calling the function and the OS mutt is running on.
By implementing the LFSR113 function by Pierre L'Ecuyer mutt gets a fast and high quality PRNG that, given the same seeds, results in the same output no matter the environment mutt is running on. --- Makefile.am | 3 ++- configure.ac | 1 - init.c | 18 +-------------- main.c | 1 - mutt_random.c | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++ mutt_random.h | 30 ++++++++++++++++++++++++ muttlib.c | 9 ++++---- protos.h | 10 -------- sendlib.c | 3 ++- 9 files changed, 104 insertions(+), 35 deletions(-) create mode 100644 mutt_random.c create mode 100644 mutt_random.h diff --git a/Makefile.am b/Makefile.am index 6f166d5a..f7a2816c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -39,7 +39,8 @@ mutt_SOURCES = \ score.c send.c sendlib.c signal.c sort.c \ status.c system.c thread.c charset.c history.c lib.c \ muttlib.c editmsg.c mbyte.c \ - url.c ascii.c crypt-mod.c crypt-mod.h safe_asprintf.c + url.c ascii.c crypt-mod.c crypt-mod.h safe_asprintf.c \ + mutt_random.c nodist_mutt_SOURCES = $(BUILT_SOURCES) diff --git a/configure.ac b/configure.ac index 7906ce35..0f65fc56 100644 --- a/configure.ac +++ b/configure.ac @@ -34,7 +34,6 @@ AC_PROG_CPP AC_PROG_MAKE_SET AC_PROG_INSTALL AC_PROG_MKDIR_P -AC_PROG_RANLIB AC_CHECK_TOOL(AR, ar, ar) AC_CACHE_CHECK([for GNU make], diff --git a/init.c b/init.c index 26f8afcf..7b0756fd 100644 --- a/init.c +++ b/init.c @@ -33,6 +33,7 @@ #include "mutt_crypt.h" #include "mutt_idna.h" #include "group.h" +#include "mutt_random.h" #if defined(USE_SSL) #include "mutt_ssl.h" @@ -3424,23 +3425,6 @@ static int mutt_execute_commands (LIST *p) return 0; } -static void mutt_srandom (void) -{ - struct timeval tv; - unsigned seed; - - gettimeofday(&tv, NULL); - /* POSIX.1-2008 states that seed is 'unsigned' without specifying its width. - * Use as many of the lower order bits from the current time of day as the seed. - * If the upper bound is truncated, that is fine. - * - * tv_sec is integral of type integer or float. Cast to 'long long' before - * bitshift in case it is a float. - */ - seed = ((LONGLONG) tv.tv_sec << 20) | tv.tv_usec; - srandom(seed); -} - static char* mutt_find_cfg (const char *home, const char *xdg_cfg_home) { const char* names[] = diff --git a/main.c b/main.c index 293f7172..d5b85525 100644 --- a/main.c +++ b/main.c @@ -665,7 +665,6 @@ int main (int argc, char **argv, char **environ) mutt_error = mutt_nocurses_error; mutt_message = mutt_nocurses_error; - SRAND (time (NULL)); umask (077); memset (Options, 0, sizeof (Options)); diff --git a/mutt_random.c b/mutt_random.c new file mode 100644 index 00000000..ab0e6a84 --- /dev/null +++ b/mutt_random.c @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2020 Remco Rijnders <re...@webconquest.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mutt_random.h" + +/* Return 32 random bits. This is an implementation of the LFSR113 PNRG + algorithm by Pierre L'Ecuyer. */ +u_int32_t mutt_random32 (void) +{ + u_int32_t b; + b = ((z1 << 6) ^ z1) >> 13; + z1 = ((z1 & 4294967294U) << 18) ^ b; + b = ((z2 << 2) ^ z2) >> 27; + z2 = ((z2 & 4294967288U) << 2) ^ b; + b = ((z3 << 13) ^ z3) >> 21; + z3 = ((z3 & 4294967280U) << 7) ^ b; + b = ((z4 << 3) ^ z4) >> 12; + z4 = ((z4 & 4294967168U) << 13) ^ b; + + return (z1 ^ z2 ^ z3 ^ z4); +} + +/* Initialize the four seeds for our PRNG algorithm */ +void mutt_srandom(void) +{ + struct timeval tv; + gettimeofday(&tv, NULL); + /* POSIX.1-2008 states that seed is 'unsigned' without specifying its width. + * Use as many of the lower order bits from the current time of day as the seed. + * If the upper bound is truncated, that is fine. + * + * tv_sec is integral of type integer or float. Cast to 'u_int32_t' before + * bitshift in case it is a float. + */ + z1 = ((u_int32_t) tv.tv_sec << 20) | tv.tv_usec; + z2 = getpid(); + z3 = getppid(); + z4 = (intptr_t) &z4; +} + +/* Generate and Base64 encode 96 random bits and fill the buffer output_B64 with + the result. */ +void mutt_base64_random96(char output_B64[static 17]) +{ + u_int32_t randombits[3] = { mutt_random32(), mutt_random32(), mutt_random32() }; + + mutt_to_base64((unsigned char *) output_B64, (unsigned char *) randombits, + 12, 17); +} diff --git a/mutt_random.h b/mutt_random.h new file mode 100644 index 00000000..bdb76f66 --- /dev/null +++ b/mutt_random.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2020 Remco Rijnders <re...@webconquest.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <sys/time.h> +#include <sys/types.h> +#include <string.h> +#include <unistd.h> + +extern void mutt_to_base64 (unsigned char*, const unsigned char*, size_t, size_t); + +void mutt_srandom(void); +u_int32_t mutt_random32(void); +void mutt_base64_random96(char output_B64[static 17]); + +u_int32_t z1, z2, z3, z4; diff --git a/muttlib.c b/muttlib.c index 4670a931..9b500f1d 100644 --- a/muttlib.c +++ b/muttlib.c @@ -34,6 +34,7 @@ #endif #include "mutt_crypt.h" +#include "mutt_random.h" #include <string.h> #include <ctype.h> @@ -917,9 +918,9 @@ void mutt_merge_envelopes(ENVELOPE* base, ENVELOPE** extra) void _mutt_buffer_mktemp (BUFFER *buf, const char *prefix, const char *suffix, const char *src, int line) { - mutt_buffer_printf (buf, "%s/%s-%s-%d-%d-%ld%ld%s%s", + mutt_buffer_printf (buf, "%s/%s-%s-%d-%d-%u%u%s%s", NONULL (Tempdir), NONULL (prefix), NONULL (Hostname), - (int) getuid (), (int) getpid (), random (), random (), + (int) getuid (), (int) getpid (), mutt_random32(), mutt_random32(), suffix ? "." : "", NONULL (suffix)); dprint (3, (debugfile, "%s:%d: mutt_mktemp returns \"%s\".\n", src, line, mutt_b2s (buf))); if (unlink (mutt_b2s (buf)) && errno != ENOENT) @@ -930,9 +931,9 @@ void _mutt_buffer_mktemp (BUFFER *buf, const char *prefix, const char *suffix, void _mutt_mktemp (char *s, size_t slen, const char *prefix, const char *suffix, const char *src, int line) { - size_t n = snprintf (s, slen, "%s/%s-%s-%d-%d-%ld%ld%s%s", + size_t n = snprintf (s, slen, "%s/%s-%s-%d-%d-%u%u%s%s", NONULL (Tempdir), NONULL (prefix), NONULL (Hostname), - (int) getuid (), (int) getpid (), random (), random (), + (int) getuid (), (int) getpid (), mutt_random32(), mutt_random32(), suffix ? "." : "", NONULL (suffix)); if (n >= slen) dprint (1, (debugfile, "%s:%d: ERROR: insufficient buffer space to hold temporary filename! slen=%zu but need %zu\n", diff --git a/protos.h b/protos.h index 5f57fb89..90021655 100644 --- a/protos.h +++ b/protos.h @@ -461,16 +461,6 @@ void mutt_pattern_free (pattern_t **pat); #define LONGLONG long #endif -#ifdef HAVE_SRAND48 -#define LRAND lrand48 -#define SRAND srand48 -#define DRAND drand48 -#else -#define LRAND rand -#define SRAND srand -#define DRAND (double)rand -#endif /* HAVE_SRAND48 */ - /* HP-UX, ConvexOS and UNIXware don't have this macro */ #ifndef S_ISLNK #define S_ISLNK(x) (((x) & S_IFMT) == S_IFLNK ? 1 : 0) diff --git a/sendlib.c b/sendlib.c index cdec5beb..f5389d8b 100644 --- a/sendlib.c +++ b/sendlib.c @@ -34,6 +34,7 @@ #include "pager.h" #include "charset.h" #include "mutt_crypt.h" +#include "mutt_random.h" #include "mutt_idna.h" #include "buffy.h" #include "send.h" @@ -520,7 +521,7 @@ void mutt_generate_boundary (PARAMETER **parm) rs[BOUNDARYLEN] = 0; for (i=0;i<BOUNDARYLEN;i++) - *p++ = B64Chars[LRAND() % sizeof (B64Chars)]; + *p++ = B64Chars[mutt_random32() % sizeof (B64Chars)]; *p = 0; mutt_set_parameter ("boundary", rs, parm); -- 2.26.2