summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEli Zaretskii <eliz@gnu.org>2016-01-15 11:47:55 +0200
committerEli Zaretskii <eliz@gnu.org>2016-01-15 11:47:55 +0200
commit3ffe81e245d854a694ae1734f1b6a995bdc5e724 (patch)
tree2d8fccecc5b404f8fb71e15fe0ea6477a09a1857
parentfee0526a189f43e8470d78e8374bd425890fbe6f (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.ac16
-rw-r--r--doc/lispref/numbers.texi4
-rw-r--r--etc/NEWS8
-rw-r--r--src/fns.c3
-rw-r--r--src/sysdep.c31
-rw-r--r--src/w32.c32
-rw-r--r--src/w32.h3
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.
diff --git a/etc/NEWS b/etc/NEWS
index 21aee20089..9e635798c5 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 977229b97b..19fa44086c 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 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);
}
diff --git a/src/w32.c b/src/w32.c
index ea3a9dafad..7884bad619 100644
--- a/src/w32.c
+++ b/src/w32.c
@@ -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 */
diff --git a/src/w32.h b/src/w32.h
index 501056d38c..ba3fec8b7e 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 <gnutls/gnutls.h>