Commit 874a0e51 authored by Guillem Jover's avatar Guillem Jover

Update arc4random module from OpenBSD and LibreSSL

Rework arc4random_stir() and arc4random_addrandom() code over the new
internal API, and documentation in the man page. Adapt the code to the
local build system.

Fixes: https://bugs.freedesktop.org/show_bug.cgi?id=85827
parent 9a9a8b2d
...@@ -129,7 +129,7 @@ AC_LINK_IFELSE( ...@@ -129,7 +129,7 @@ AC_LINK_IFELSE(
[AC_MSG_RESULT([no])]) [AC_MSG_RESULT([no])])
AC_CHECK_FUNCS([clearenv dirfd fopencookie __fpurge \ AC_CHECK_FUNCS([clearenv dirfd fopencookie __fpurge \
getentropy getexecname getline \ getauxval getentropy getexecname getline \
pstat_getproc sysconf]) pstat_getproc sysconf])
AM_CONDITIONAL(HAVE_GETENTROPY, [test "x$ac_cv_func_getentropy" = "xtrue"]) AM_CONDITIONAL(HAVE_GETENTROPY, [test "x$ac_cv_func_getentropy" = "xtrue"])
......
.\" $OpenBSD: arc4random.3,v 1.2 1997/04/27 22:40:25 angelos Exp $ .\" $OpenBSD: arc4random.3,v 1.34 2014/07/19 16:11:16 naddy Exp $
.\"
.\" Copyright 1997 Niels Provos <provos@physnet.uni-hamburg.de> .\" Copyright 1997 Niels Provos <provos@physnet.uni-hamburg.de>
.\" All rights reserved. .\" All rights reserved.
.\" .\"
...@@ -28,9 +29,8 @@ ...@@ -28,9 +29,8 @@
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\" .\"
.\" Manual page, using -mandoc macros .\" Manual page, using -mandoc macros
.\" $FreeBSD$
.\" .\"
.Dd April 15, 1997 .Dd $Mdocdate: July 19 2014 $
.Dt ARC4RANDOM 3 .Dt ARC4RANDOM 3
.Os .Os
.Sh NAME .Sh NAME
...@@ -56,45 +56,59 @@ ...@@ -56,45 +56,59 @@
.Ft void .Ft void
.Fn arc4random_addrandom "unsigned char *dat" "int datlen" .Fn arc4random_addrandom "unsigned char *dat" "int datlen"
.Sh DESCRIPTION .Sh DESCRIPTION
This family of functions provides higher quality data than those
described in
.Xr rand 3 ,
.Xr random 3 ,
and
.Xr rand48 3 .
.Pp
Use of these functions is encouraged for almost all random number
consumption because the other interfaces are deficient in either
quality, portability, standardization, or availability.
These functions can be called in almost all coding environments,
including
.Xr pthreads 3
and
.Xr chroot 2 .
.Pp
High quality 32-bit pseudo-random numbers are generated very quickly.
On each call, a cryptographic pseudo-random number generator is used
to generate a new result.
One data pool is used for all consumers in a process, so that consumption
under program flow can act as additional stirring.
The subsystem is re-seeded from the kernel random number subsystem using
.Xr getentropy 2
on a regular basis, and also upon
.Xr fork 2 .
.Pp
The The
.Fn arc4random .Fn arc4random
function uses the key stream generator employed by the function returns a single 32-bit value.
arc4 cipher, which uses 8*8 8 bit S-Boxes.
The S-Boxes
can be in about
.if t 2\u\s71700\s10\d
.if n (2**1700)
states.
The
.Fn arc4random
function returns pseudo-random numbers in the range of 0 to
.if t 2\u\s731\s10\d\(mi1,
.if n (2**32)\(mi1,
and therefore has twice the range of
.Xr rand 3
and
.Xr random 3 .
.Pp .Pp
The
.Fn arc4random_buf .Fn arc4random_buf
function fills the region function fills the region
.Fa buf .Fa buf
of length of length
.Fa nbytes .Fa nbytes
with ARC4-derived random data. with random data.
.Pp .Pp
.Fn arc4random_uniform .Fn arc4random_uniform
will return a uniformly distributed random number less than will return a single 32-bit value, uniformly distributed but less than
.Fa upper_bound . .Fa upper_bound .
.Fn arc4random_uniform This is recommended over constructions like
is recommended over constructions like
.Dq Li arc4random() % upper_bound .Dq Li arc4random() % upper_bound
as it avoids "modulo bias" when the upper bound is not a power of two. as it avoids "modulo bias" when the upper bound is not a power of two.
In the worst case, this function may consume multiple iterations
to ensure uniformity; see the source code to understand the problem
and solution.
.Pp .Pp
The The
.Fn arc4random_stir .Fn arc4random_stir
function reads data from function reads data from
.Pa /dev/urandom .Xr getentropy 2
and uses it to permute the S-Boxes via and uses it to re-seed the subsystem via
.Fn arc4random_addrandom . .Fn arc4random_addrandom .
.Pp .Pp
There is no need to call There is no need to call
...@@ -103,26 +117,22 @@ before using ...@@ -103,26 +117,22 @@ before using
.Fn arc4random .Fn arc4random
functions family, since functions family, since
they automatically initialize themselves. they automatically initialize themselves.
.Sh EXAMPLES .Sh RETURN VALUES
The following produces a drop-in replacement for the traditional These functions are always successful, and no return value is
.Fn rand reserved to indicate an error.
and
.Fn random
functions using
.Fn arc4random :
.Pp
.Dl "#define foo4random() (arc4random() % ((unsigned)RAND_MAX + 1))"
.Sh SEE ALSO .Sh SEE ALSO
.Xr rand 3 , .Xr rand 3 ,
.Xr random 3 , .Xr rand48 3 ,
.Xr srandomdev 3 .Xr random 3
.Sh HISTORY .Sh HISTORY
.Pa RC4 These functions first appeared in
has been designed by RSA Data Security, Inc. .Ox 2.1 .
It was posted anonymously .Pp
to the USENET and was confirmed to be equivalent by several sources who The original version of this random number generator used the
had access to the original cipher. RC4 (also known as ARC4) algorithm.
Since In
.Pa RC4 .Ox 5.5
used to be a trade secret, the cipher is now referred to as it was replaced with the ChaCha20 cipher, and it may be replaced
.Pa ARC4 . again in the future as cryptographic techniques advance.
A good mnemonic is
.Dq A Replacement Call for Random .
...@@ -56,7 +56,14 @@ libbsd_la_LDFLAGS = \ ...@@ -56,7 +56,14 @@ libbsd_la_LDFLAGS = \
-version-number $(LIBBSD_ABI) -version-number $(LIBBSD_ABI)
libbsd_la_SOURCES = \ libbsd_la_SOURCES = \
arc4random.c \ arc4random.c \
arc4random.h \
arc4random_bsd.h \
arc4random_linux.h \
arc4random_unix.h \
arc4random_openbsd.h \
arc4random_uniform.c \
bsd_getopt.c \ bsd_getopt.c \
chacha_private.h \
closefrom.c \ closefrom.c \
dehumanize_number.c \ dehumanize_number.c \
err.c \ err.c \
......
/* $OpenBSD: arc4random.c,v 1.53 2015/09/10 18:53:50 bcook Exp $ */
/* /*
* Copyright (c) 1996, David Mazieres <dm@uun.org> * Copyright (c) 1996, David Mazieres <dm@uun.org>
* Copyright (c) 2008, Damien Miller <djm@openbsd.org> * Copyright (c) 2008, Damien Miller <djm@openbsd.org>
* Copyright (c) 2013, Markus Friedl <markus@openbsd.org>
* Copyright (c) 2014, Theo de Raadt <deraadt@openbsd.org>
* Copyright (c) 2015, Guillem Jover <guillem@hadrons.org>
* *
* Permission to use, copy, modify, and distribute this software for any * Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above * purpose with or without fee is hereby granted, provided that the above
...@@ -16,281 +21,192 @@ ...@@ -16,281 +21,192 @@
*/ */
/* /*
* Arc4 random number generator for OpenBSD. * ChaCha based random number generator for OpenBSD.
*
* This code is derived from section 17.1 of Applied Cryptography,
* second edition, which describes a stream cipher allegedly
* compatible with RSA Labs "RC4" cipher (the actual description of
* which is a trade secret). The same algorithm is used as a stream
* cipher called "arcfour" in Tatu Ylonen's ssh package.
*
* Here the stream cipher has been modified always to include the time
* when initializing the state. That makes it impossible to
* regenerate the same random sequence twice, so this can't be used
* for encryption, but will generate good random numbers.
*
* RC4 is a registered trademark of RSA Laboratories.
*/ */
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/types.h>
#include <sys/time.h>
#include <stdlib.h>
#include <fcntl.h> #include <fcntl.h>
#include <limits.h>
#include <signal.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h> #include <unistd.h>
#include <pthread.h> #include <sys/types.h>
#include <sys/time.h>
struct arc4_stream { #define KEYSTREAM_ONLY
uint8_t i; #include "chacha_private.h"
uint8_t j;
uint8_t s[256];
};
#define RANDOMDEV "/dev/urandom" #define minimum(a, b) ((a) < (b) ? (a) : (b))
#define KEYSIZE 128
#ifdef __REENTRANT #if defined(__GNUC__) || defined(_MSC_VER)
static pthread_mutex_t arc4random_mtx = PTHREAD_MUTEX_INITIALIZER; #define inline __inline
#define THREAD_LOCK() pthread_mutex_lock(&arc4random_mtx) #else /* __GNUC__ || _MSC_VER */
#define THREAD_UNLOCK() pthread_mutex_unlock(&arc4random_mtx) #define inline
#else #endif /* !__GNUC__ && !_MSC_VER */
#define THREAD_LOCK()
#define THREAD_UNLOCK() #define KEYSZ 32
#endif #define IVSZ 8
#define BLOCKSZ 64
#define RSBUFSZ (16*BLOCKSZ)
/* Marked MAP_INHERIT_ZERO, so zero'd out in fork children. */
static struct _rs {
size_t rs_have; /* valid bytes at end of rs_buf */
size_t rs_count; /* bytes till reseed */
} *rs;
static struct arc4_stream rs; /* Maybe be preserved in fork children, if _rs_allocate() decides. */
static int rs_initialized; static struct _rsx {
static int rs_stired; chacha_ctx rs_chacha; /* chacha context for random keystream */
static int arc4_count; u_char rs_buf[RSBUFSZ]; /* keystream blocks */
} *rsx;
static inline uint8_t arc4_getbyte(void); static inline int _rs_allocate(struct _rs **, struct _rsx **);
static void arc4_stir(void); static inline void _rs_forkdetect(void);
#include "arc4random.h"
static inline void static inline void
arc4_init(void) _rs_init(u_char *buf, size_t n)
{ {
int n; if (n < KEYSZ + IVSZ)
return;
for (n = 0; n < 256; n++) if (rs == NULL) {
rs.s[n] = n; if (_rs_allocate(&rs, &rsx) == -1)
rs.i = 0; abort();
rs.j = 0; }
chacha_keysetup(&rsx->rs_chacha, buf, KEYSZ * 8, 0);
chacha_ivsetup(&rsx->rs_chacha, buf + KEYSZ);
} }
static inline void static inline void
arc4_addrandom(u_char *dat, int datlen) _rs_rekey(u_char *dat, size_t datlen)
{ {
int n; #ifndef KEYSTREAM_ONLY
uint8_t si; memset(rsx->rs_buf, 0, sizeof(rsx->rs_buf));
#endif
rs.i--; /* fill rs_buf with the keystream */
for (n = 0; n < 256; n++) { chacha_encrypt_bytes(&rsx->rs_chacha, rsx->rs_buf,
rs.i = (rs.i + 1); rsx->rs_buf, sizeof(rsx->rs_buf));
si = rs.s[rs.i]; /* mix in optional user provided data */
rs.j = (rs.j + si + dat[n % datlen]); if (dat) {
rs.s[rs.i] = rs.s[rs.j]; size_t i, m;
rs.s[rs.j] = si;
m = minimum(datlen, KEYSZ + IVSZ);
for (i = 0; i < m; i++)
rsx->rs_buf[i] ^= dat[i];
} }
rs.j = rs.i; /* immediately reinit for backtracking resistance */
_rs_init(rsx->rs_buf, KEYSZ + IVSZ);
memset(rsx->rs_buf, 0, KEYSZ + IVSZ);
rs->rs_have = sizeof(rsx->rs_buf) - KEYSZ - IVSZ;
} }
static void static void
arc4_stir(void) _rs_stir(void)
{ {
int done, fd, n; u_char rnd[KEYSZ + IVSZ];
struct {
struct timeval tv;
pid_t pid;
uint8_t rnd[KEYSIZE];
} rdat;
fd = open(RANDOMDEV, O_RDONLY, 0);
done = 0;
if (fd >= 0) {
if (read(fd, &rdat, KEYSIZE) == KEYSIZE)
done = 1;
(void)close(fd);
}
if (!done) {
(void)gettimeofday(&rdat.tv, NULL);
rdat.pid = getpid();
/* We'll just take whatever was on the stack too... */
}
arc4_addrandom((u_char *)&rdat, KEYSIZE);
/* if (getentropy(rnd, sizeof rnd) == -1)
* Throw away the first N bytes of output, as suggested in the _getentropy_fail();
* paper "Weaknesses in the Key Scheduling Algorithm of RC4"
* by Fluher, Mantin, and Shamir. N=1024 is based on
* suggestions in the paper "(Not So) Random Shuffles of RC4"
* by Ilya Mironov.
*/
for (n = 0; n < 1024; n++)
(void) arc4_getbyte();
arc4_count = 1600000;
}
static inline uint8_t if (!rs)
arc4_getbyte(void) _rs_init(rnd, sizeof(rnd));
{ else
uint8_t si, sj; _rs_rekey(rnd, sizeof(rnd));
explicit_bzero(rnd, sizeof(rnd)); /* discard source seed */
rs.i = (rs.i + 1); /* invalidate rs_buf */
si = rs.s[rs.i]; rs->rs_have = 0;
rs.j = (rs.j + si); memset(rsx->rs_buf, 0, sizeof(rsx->rs_buf));
sj = rs.s[rs.j];
rs.s[rs.i] = sj;
rs.s[rs.j] = si;
return (rs.s[(si + sj) & 0xff]); rs->rs_count = 1600000;
} }
static inline uint32_t static inline void
arc4_getword(void) _rs_stir_if_needed(size_t len)
{ {
uint32_t val; _rs_forkdetect();
if (!rs || rs->rs_count <= len)
val = arc4_getbyte() << 24; _rs_stir();
val |= arc4_getbyte() << 16; if (rs->rs_count <= len)
val |= arc4_getbyte() << 8; rs->rs_count = 0;
val |= arc4_getbyte(); else
rs->rs_count -= len;
return (val);
} }
static void static inline void
arc4_check_init(void) _rs_random_buf(void *_buf, size_t n)
{ {
if (!rs_initialized) { u_char *buf = (u_char *)_buf;
arc4_init(); u_char *keystream;
rs_initialized = 1; size_t m;
_rs_stir_if_needed(n);
while (n > 0) {
if (rs->rs_have > 0) {
m = minimum(n, rs->rs_have);
keystream = rsx->rs_buf + sizeof(rsx->rs_buf)
- rs->rs_have;
memcpy(buf, keystream, m);
memset(keystream, 0, m);
buf += m;
n -= m;
rs->rs_have -= m;
}
if (rs->rs_have == 0)
_rs_rekey(NULL, 0);
} }
} }
static inline void static inline void
arc4_check_stir(void) _rs_random_u32(uint32_t *val)
{ {
if (!rs_stired || arc4_count <= 0) { u_char *keystream;
arc4_stir();
rs_stired = 1; _rs_stir_if_needed(sizeof(*val));
} if (rs->rs_have < sizeof(*val))
_rs_rekey(NULL, 0);
keystream = rsx->rs_buf + sizeof(rsx->rs_buf) - rs->rs_have;
memcpy(val, keystream, sizeof(*val));
memset(keystream, 0, sizeof(*val));
rs->rs_have -= sizeof(*val);
} }
void void
arc4random_stir(void) arc4random_stir(void)
{ {
THREAD_LOCK(); _ARC4_LOCK();
arc4_check_init(); _rs_stir();
arc4_stir(); _ARC4_UNLOCK();
rs_stired = 1;
THREAD_UNLOCK();
} }
void void
arc4random_addrandom(u_char *dat, int datlen) arc4random_addrandom(u_char *dat, int datlen)
{ {
THREAD_LOCK(); _ARC4_LOCK();
arc4_check_init(); _rs_stir_if_needed(datlen);
arc4_check_stir(); _rs_rekey(dat, datlen);
arc4_addrandom(dat, datlen); _ARC4_UNLOCK();
THREAD_UNLOCK();
} }
uint32_t uint32_t
arc4random(void) arc4random(void)
{ {
uint32_t rnd; uint32_t val;
THREAD_LOCK();
arc4_check_init();
arc4_check_stir();
rnd = arc4_getword();
arc4_count -= 4;
THREAD_UNLOCK();
return (rnd); _ARC4_LOCK();
_rs_random_u32(&val);
_ARC4_UNLOCK();
return val;
} }
void void
arc4random_buf(void *_buf, size_t n) arc4random_buf(void *buf, size_t n)
{
u_char *buf = (u_char *)_buf;
THREAD_LOCK();
arc4_check_init();
while (n--) {
arc4_check_stir();
buf[n] = arc4_getbyte();
arc4_count--;
}
THREAD_UNLOCK();
}
/*
* Calculate a uniformly distributed random number less than upper_bound
* avoiding "modulo bias".
*
* Uniformity is achieved by generating new random numbers until the one
* returned is outside the range [0, 2**32 % upper_bound). This
* guarantees the selected random number will be inside
* [2**32 % upper_bound, 2**32) which maps back to [0, upper_bound)
* after reduction modulo upper_bound.
*/
uint32_t
arc4random_uniform(uint32_t upper_bound)
{
uint32_t r, min;
if (upper_bound < 2)
return (0);
#if (ULONG_MAX > 0xffffffffUL)
min = 0x100000000UL % upper_bound;
#else
/* Calculate (2**32 % upper_bound) avoiding 64-bit math */
if (upper_bound > 0x80000000)
min = 1 + ~upper_bound; /* 2**32 - upper_bound */
else {
/* (2**32 - (x * 2)) % x == 2**32 % x when x <= 2**31 */
min = ((0xffffffff - (upper_bound * 2)) + 1) % upper_bound;
}
#endif
/*
* This could theoretically loop forever but each retry has
* p > 0.5 (worst case, usually far better) of selecting a
* number inside the range we need, so it should rarely need
* to re-roll.
*/
for (;;) {
r = arc4random();
if (r >= min)
break;
}
return (r % upper_bound);
}
#if 0
/*-------- Test code for i386 --------*/
#include <stdio.h>
#include <machine/pctr.h>
int
main(int argc, char **argv)
{ {
const int iter = 1000000; _ARC4_LOCK();
int i; _rs_random_buf(buf, n);
pctrval v; _ARC4_UNLOCK();
v = rdtsc();
for (i = 0; i < iter; i++)
arc4random();
v = rdtsc() - v;
v /= iter;
printf("%qd cycles\n", v);
} }
#endif
#ifndef LIBBSD_ARC4RANDOM_H
#define LIBBSD_ARC4RANDOM_H
#include <sys/param.h>
int
getentropy(void *buf, size_t len);
#if defined(__linux__)
#include "arc4random_linux.h"
#elif defined(__FreeBSD__)
#include "arc4random_bsd.h"
#elif defined(__NetBSD__)
#include "arc4random_bsd.h"
#elif defined(__OpenBSD__)
#include "arc4random_openbsd.h"
#elif defined(__sun)
#include "arc4random_unix.h"
#elif defined(__APPLE__)
#include "arc4random_unix.h"
#elif defined(_AIX)
#include "arc4random_unix.h"
#elif defined(__hpux)
#include "arc4random_unix.h"
#else
#error "No arc4random hooks defined for this platform."
#endif
#endif
/* $OpenBSD: arc4random_freebsd.h,v 1.2 2015/01/15 06:57:18 deraadt Exp $ */
/*
* Copyright (c) 1996, David Mazieres <dm@uun.org>
* Copyright (c) 2008, Damien Miller <djm@openbsd.org>
* Copyright (c) 2013, Markus Friedl <markus@openbsd.org>
* Copyright (c) 2014, Theo de Raadt <deraadt@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
* Stub functions for portability.
*/
#include <sys/mman.h>
#include <pthread.h>
#include <signal.h>
static pthread_mutex_t arc4random_mtx = PTHREAD_MUTEX_INITIALIZER;
#define _ARC4_LOCK() pthread_mutex_lock(&arc4random_mtx)
#define _ARC4_UNLOCK() pthread_mutex_unlock(&arc4random_mtx)
/*
* Unfortunately, pthread_atfork() is broken on FreeBSD (at least 9 and 10) if
* a program does not link to -lthr. Callbacks registered with pthread_atfork()
* appear to fail silently. So, it is not always possible to detect a PID
* wraparound.
*/
#define _ARC4_ATFORK(f) pthread_atfork(NULL, NULL, (f))
static inline void
_getentropy_fail(void)
{
raise(SIGKILL);
}
static volatile sig_atomic_t _rs_forked;
static inline void
_rs_forkhandler(void)
{
_rs_forked = 1;
}
static inline void
_rs_forkdetect(void)
{
static pid_t _rs_pid = 0;
pid_t pid = getpid();
if (_rs_pid == 0 || _rs_pid != pid || _rs_forked) {
_rs_pid = pid;
_rs_forked = 0;
if (rs)
memset(rs, 0, sizeof(*rs));
}
}
static inline int
_rs_allocate(struct _rs **rsp, struct _rsx **rsxp)
{
if ((*rsp = mmap(NULL, sizeof(**rsp), PROT_READ|PROT_WRITE,
MAP_ANON|MAP_PRIVATE, -1, 0)) == MAP_FAILED)
return (-1);
if ((*rsxp = mmap(NULL, sizeof(**rsxp), PROT_READ|PROT_WRITE,
MAP_ANON|MAP_PRIVATE, -1, 0)) == MAP_FAILED) {
munmap(*rsp, sizeof(**rsp));
return (-1);
}