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)
This commit is contained in:
Eli Zaretskii 2016-01-15 11:47:55 +02:00
parent fee0526a18
commit 3ffe81e245
7 changed files with 93 additions and 4 deletions

View 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.

View 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.

View file

@ -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'.

View file

@ -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.

View 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);
}

View file

@ -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 */

View file

@ -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>