diff options
author | Eli Zaretskii <eliz@gnu.org> | 2016-01-15 11:47:55 +0200 |
---|---|---|
committer | Eli Zaretskii <eliz@gnu.org> | 2016-01-15 11:47:55 +0200 |
commit | 3ffe81e245d854a694ae1734f1b6a995bdc5e724 (patch) | |
tree | 2d8fccecc5b404f8fb71e15fe0ea6477a09a1857 | |
parent | fee0526a189f43e8470d78e8374bd425890fbe6f (diff) |
Make 'random' seeds cryptographically secure if possible
* configure.ac: Check for "/dev/urandom".
* src/sysdep.c (init_random) [HAVE_DEV_URANDOM]: Read the stream
for the seed from "/dev/urandom".
[WINDOWSNT]: Obtain the stream for the seed from w32 APIs.
* src/fns.c (Frandom): Update the doc string to indicate that
system entropy is used when available.
* src/w32.c: Include wincrypt.h.
(w32_init_crypt_random, w32_init_random): New functions, use the
CryptGenRandom API.
(globals_of_w32): Initialize w32_crypto_hprov handle to zero.
* src/w32.h (w32_init_random): Add prototype.
* doc/lispref/numbers.texi (Random Numbers): Document more details
about 't' as the argument to 'random'.
* etc/NEWS: Mention that '(random t)' now uses a cryptographically
strong seed if possible.
(Bug#22202)
-rw-r--r-- | configure.ac | 16 | ||||
-rw-r--r-- | doc/lispref/numbers.texi | 4 | ||||
-rw-r--r-- | etc/NEWS | 8 | ||||
-rw-r--r-- | src/fns.c | 3 | ||||
-rw-r--r-- | src/sysdep.c | 31 | ||||
-rw-r--r-- | src/w32.c | 32 | ||||
-rw-r--r-- | src/w32.h | 3 |
7 files changed, 93 insertions, 4 deletions
diff --git a/configure.ac b/configure.ac index 8c01abac9c..6c9b621dd8 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 20d3c4290f..3a9483af96 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. @@ -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'. @@ -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 a78c4c64c8..1fa422947e 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); } @@ -224,6 +224,8 @@ typedef struct _REPARSE_DATA_BUFFER { #include <iphlpapi.h> /* should be after winsock2.h */ +#include <wincrypt.h> + #include <c-strcase.h> #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 */ @@ -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 <gnutls/gnutls.h> |