diff --git a/configure.ac b/configure.ac index 8c01abac9c6..6c9b621dd82 100644 --- a/configure.ac +++ b/configure.ac @@ -4153,6 +4153,22 @@ fi AC_TYPE_MBSTATE_T +AC_MSG_CHECKING([whether "/dev/urandom" is available]) +dev_urandom=no +dnl MSYS, being a Cygwin fork, thinks "/dev/urandom" does exist, so +dnl don't check this for the MinGW builds. +if test "${opsys}" != "mingw32"; then + if test -r "/dev/urandom"; then + AC_DEFINE(HAVE_DEV_URANDOM, 1, [Define if the system supports the "/dev/urandom" device.]) + dev_urandom=yes + fi +fi +if test $dev_urandom = yes; then + AC_MSG_RESULT(yes) +else + AC_MSG_RESULT(no) +fi + dnl Fixme: AC_SYS_POSIX_TERMIOS should probably be used, but it's not clear dnl how the tty code is related to POSIX and/or other versions of termios. dnl The following looks like a useful start. diff --git a/doc/lispref/numbers.texi b/doc/lispref/numbers.texi index 20d3c4290f3..3a9483af967 100644 --- a/doc/lispref/numbers.texi +++ b/doc/lispref/numbers.texi @@ -1252,7 +1252,9 @@ any integer representable in Lisp, i.e., an integer between (@pxref{Integer Basics}). If @var{limit} is @code{t}, it means to choose a new seed as if Emacs -were restarting. +were restarting. The new seed will be set from the system entropy, if +that is available, or from the current time and Emacs process's PID +(@pxref{System Environment, emacs-pid}) if not. If @var{limit} is a string, it means to choose a new seed based on the string's contents. diff --git a/etc/NEWS b/etc/NEWS index 21aee20089e..9e635798c58 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -214,6 +214,14 @@ for use in Emacs bug reports. hiding character but the default `.' can be used by let-binding the variable `read-hide-char'. ++++ +** The Emacs pseudo-random number generator can be securely seeded. +On system where Emacs can access the system entropy or some other +cryptographically secure random stream, it now uses that when `random' +is called with its argument `t'. This allows cryptographically strong +random values; in particular, the Emacs server now uses this facility +to produce its authentication key. + --- ** New input methods: `tamil-dvorak' and `programmer-dvorak'. diff --git a/src/fns.c b/src/fns.c index 977229b97b7..19fa44086c9 100644 --- a/src/fns.c +++ b/src/fns.c @@ -50,7 +50,8 @@ All integers representable in Lisp, i.e. between `most-negative-fixnum' and `most-positive-fixnum', inclusive, are equally likely. With positive integer LIMIT, return random number in interval [0,LIMIT). -With argument t, set the random number seed from the current time and pid. +With argument t, set the random number seed from the system's entropy +pool, or from the current time and pid if entropy is unavailable. With a string argument, set the seed based on the string's contents. Other values of LIMIT are ignored. diff --git a/src/sysdep.c b/src/sysdep.c index a78c4c64c81..1fa422947ed 100644 --- a/src/sysdep.c +++ b/src/sysdep.c @@ -2095,8 +2095,35 @@ seed_random (void *seed, ptrdiff_t seed_size) void init_random (void) { - struct timespec t = current_timespec (); - uintmax_t v = getpid () ^ t.tv_sec ^ t.tv_nsec; + uintmax_t v; + struct timespec t; + bool success = false; + +#if HAVE_DEV_URANDOM + FILE *fp = fopen ("/dev/urandom", "rb"); + + if (fp) + { + int i; + + for (i = 0, v = 0; i < sizeof (uintmax_t); i++) + { + v <<= 8; + v |= fgetc (fp); + } + fclose (fp); + success = true; + } +#elif defined WINDOWSNT + if (w32_init_random (&v, sizeof v) == 0) + success = true; +#endif /* HAVE_DEV_URANDOM || WINDOWSNT */ + if (!success) + { + /* Fall back to current time value + PID. */ + t = current_timespec (); + v = getpid () ^ t.tv_sec ^ t.tv_nsec; + } seed_random (&v, sizeof v); } diff --git a/src/w32.c b/src/w32.c index ea3a9dafad5..7884bad619c 100644 --- a/src/w32.c +++ b/src/w32.c @@ -224,6 +224,8 @@ typedef struct _REPARSE_DATA_BUFFER { #include /* should be after winsock2.h */ +#include + #include #include "w32.h" @@ -2093,6 +2095,34 @@ init_user_info (void) CloseHandle (token); } +static HCRYPTPROV w32_crypto_hprov; +static int +w32_init_crypt_random (void) +{ + if (!CryptAcquireContext (&w32_crypto_hprov, NULL, NULL, PROV_RSA_FULL, + CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) + { + DebPrint (("CryptAcquireContext failed with error %x\n", + GetLastError ())); + w32_crypto_hprov = 0; + return -1; + } + return 0; +} + +int +w32_init_random (void *buf, ptrdiff_t buflen) +{ + if (!w32_crypto_hprov) + w32_init_crypt_random (); + if (w32_crypto_hprov) + { + if (CryptGenRandom (w32_crypto_hprov, buflen, (BYTE *)buf)) + return 0; + } + return -1; +} + int random (void) { @@ -9410,6 +9440,8 @@ globals_of_w32 (void) extern void dynlib_reset_last_error (void); dynlib_reset_last_error (); #endif + + w32_crypto_hprov = (HCRYPTPROV)0; } /* For make-serial-process */ diff --git a/src/w32.h b/src/w32.h index 501056d38c6..ba3fec8b7e6 100644 --- a/src/w32.h +++ b/src/w32.h @@ -222,6 +222,9 @@ extern int w32_memory_info (unsigned long long *, unsigned long long *, /* Compare 2 UTF-8 strings in locale-dependent fashion. */ extern int w32_compare_strings (const char *, const char *, char *, int); +/* Return a cryptographically secure seed for PRNG. */ +extern int w32_init_random (void *, ptrdiff_t); + #ifdef HAVE_GNUTLS #include