]> git.eshelyaron.com Git - emacs.git/commitdiff
Make 'random' seeds cryptographically secure if possible
authorEli Zaretskii <eliz@gnu.org>
Fri, 15 Jan 2016 09:47:55 +0000 (11:47 +0200)
committerEli Zaretskii <eliz@gnu.org>
Fri, 15 Jan 2016 09:47:55 +0000 (11:47 +0200)
* 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)

configure.ac
doc/lispref/numbers.texi
etc/NEWS
src/fns.c
src/sysdep.c
src/w32.c
src/w32.h

index 8c01abac9c6baa17fd9e4b7555d75db280807d15..6c9b621dd8243a5eb98c6e0f81bb377016a8bea1 100644 (file)
@@ -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.
index 20d3c4290f35024836269b1b1cf8e0103996c631..3a9483af96725ba02d4428ed742f6be9bde1afe4 100644 (file)
@@ -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.
index 21aee20089ec5df09cabdbeb60dc52895d287511..9e635798c58ecfb96266623f4fb240573a185693 100644 (file)
--- 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'.
 
index 977229b97b75e03d18f696b0f49125d4ba7aae95..19fa44086c9519a187f339961fa89b25f517f993 100644 (file)
--- 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.
 
index a78c4c64c815bacc6ac966f0b49cdbb7d7bafe17..1fa422947edcaef282a11f5d5f3d32a6e6ca3d70 100644 (file)
@@ -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);
 }
 
index ea3a9dafad5d48bb5bc313160ba55af9d834971b..7884bad619c4d463deb68f0cd150aafb0d3ff8f1 100644 (file)
--- 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  */
index 501056d38c603790cd651864701b9184464d254a..ba3fec8b7e6a33c6e32d56262431d176e7f159a6 100644 (file)
--- 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>