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(
[AC_MSG_RESULT([no])])
AC_CHECK_FUNCS([clearenv dirfd fopencookie __fpurge \
getentropy getexecname getline \
getauxval getentropy getexecname getline \
pstat_getproc sysconf])
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>
.\" All rights reserved.
.\"
......@@ -28,9 +29,8 @@
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\"
.\" Manual page, using -mandoc macros
.\" $FreeBSD$
.\"
.Dd April 15, 1997
.Dd $Mdocdate: July 19 2014 $
.Dt ARC4RANDOM 3
.Os
.Sh NAME
......@@ -56,45 +56,59 @@
.Ft void
.Fn arc4random_addrandom "unsigned char *dat" "int datlen"
.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
.Fn arc4random
function uses the key stream generator employed by the
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 .
function returns a single 32-bit value.
.Pp
The
.Fn arc4random_buf
function fills the region
.Fa buf
of length
.Fa nbytes
with ARC4-derived random data.
with random data.
.Pp
.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 .
.Fn arc4random_uniform
is recommended over constructions like
This is recommended over constructions like
.Dq Li arc4random() % upper_bound
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
The
.Fn arc4random_stir
function reads data from
.Pa /dev/urandom
and uses it to permute the S-Boxes via
.Xr getentropy 2
and uses it to re-seed the subsystem via
.Fn arc4random_addrandom .
.Pp
There is no need to call
......@@ -103,26 +117,22 @@ before using
.Fn arc4random
functions family, since
they automatically initialize themselves.
.Sh EXAMPLES
The following produces a drop-in replacement for the traditional
.Fn rand
and
.Fn random
functions using
.Fn arc4random :
.Pp
.Dl "#define foo4random() (arc4random() % ((unsigned)RAND_MAX + 1))"
.Sh RETURN VALUES
These functions are always successful, and no return value is
reserved to indicate an error.
.Sh SEE ALSO
.Xr rand 3 ,
.Xr random 3 ,
.Xr srandomdev 3
.Xr rand48 3 ,
.Xr random 3
.Sh HISTORY
.Pa RC4
has been designed by RSA Data Security, Inc.
It was posted anonymously
to the USENET and was confirmed to be equivalent by several sources who
had access to the original cipher.
Since
.Pa RC4
used to be a trade secret, the cipher is now referred to as
.Pa ARC4 .
These functions first appeared in
.Ox 2.1 .
.Pp
The original version of this random number generator used the
RC4 (also known as ARC4) algorithm.
In
.Ox 5.5
it was replaced with the ChaCha20 cipher, and it may be replaced
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 = \
-version-number $(LIBBSD_ABI)
libbsd_la_SOURCES = \
arc4random.c \
arc4random.h \
arc4random_bsd.h \
arc4random_linux.h \
arc4random_unix.h \
arc4random_openbsd.h \
arc4random_uniform.c \
bsd_getopt.c \
chacha_private.h \
closefrom.c \
dehumanize_number.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) 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
* purpose with or without fee is hereby granted, provided that the above
......@@ -16,281 +21,192 @@
*/
/*
* Arc4 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.
* ChaCha based random number generator for OpenBSD.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/types.h>
#include <sys/time.h>
#include <stdlib.h>
#include <fcntl.h>
#include <limits.h>
#include <signal.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/time.h>
struct arc4_stream {
uint8_t i;
uint8_t j;
uint8_t s[256];
};
#define KEYSTREAM_ONLY
#include "chacha_private.h"
#define RANDOMDEV "/dev/urandom"
#define KEYSIZE 128
#define minimum(a, b) ((a) < (b) ? (a) : (b))
#ifdef __REENTRANT
static pthread_mutex_t arc4random_mtx = PTHREAD_MUTEX_INITIALIZER;
#define THREAD_LOCK() pthread_mutex_lock(&arc4random_mtx)
#define THREAD_UNLOCK() pthread_mutex_unlock(&arc4random_mtx)
#else
#define THREAD_LOCK()
#define THREAD_UNLOCK()
#endif
#if defined(__GNUC__) || defined(_MSC_VER)
#define inline __inline
#else /* __GNUC__ || _MSC_VER */
#define inline
#endif /* !__GNUC__ && !_MSC_VER */
#define KEYSZ 32
#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;
static int rs_initialized;
static int rs_stired;
static int arc4_count;
/* Maybe be preserved in fork children, if _rs_allocate() decides. */
static struct _rsx {
chacha_ctx rs_chacha; /* chacha context for random keystream */
u_char rs_buf[RSBUFSZ]; /* keystream blocks */
} *rsx;
static inline uint8_t arc4_getbyte(void);
static void arc4_stir(void);
static inline int _rs_allocate(struct _rs **, struct _rsx **);
static inline void _rs_forkdetect(void);
#include "arc4random.h"
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++)
rs.s[n] = n;
rs.i = 0;
rs.j = 0;
if (rs == NULL) {
if (_rs_allocate(&rs, &rsx) == -1)
abort();
}
chacha_keysetup(&rsx->rs_chacha, buf, KEYSZ * 8, 0);
chacha_ivsetup(&rsx->rs_chacha, buf + KEYSZ);
}
static inline void
arc4_addrandom(u_char *dat, int datlen)
_rs_rekey(u_char *dat, size_t datlen)
{
int n;
uint8_t si;
rs.i--;
for (n = 0; n < 256; n++) {
rs.i = (rs.i + 1);
si = rs.s[rs.i];
rs.j = (rs.j + si + dat[n % datlen]);
rs.s[rs.i] = rs.s[rs.j];
rs.s[rs.j] = si;
#ifndef KEYSTREAM_ONLY
memset(rsx->rs_buf, 0, sizeof(rsx->rs_buf));
#endif
/* fill rs_buf with the keystream */
chacha_encrypt_bytes(&rsx->rs_chacha, rsx->rs_buf,
rsx->rs_buf, sizeof(rsx->rs_buf));
/* mix in optional user provided data */
if (dat) {
size_t i, m;
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
arc4_stir(void)
_rs_stir(void)
{
int done, fd, n;
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);
u_char rnd[KEYSZ + IVSZ];
/*
* Throw away the first N bytes of output, as suggested in the
* 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;
}
if (getentropy(rnd, sizeof rnd) == -1)
_getentropy_fail();
static inline uint8_t
arc4_getbyte(void)
{
uint8_t si, sj;
if (!rs)
_rs_init(rnd, sizeof(rnd));
else
_rs_rekey(rnd, sizeof(rnd));
explicit_bzero(rnd, sizeof(rnd)); /* discard source seed */
rs.i = (rs.i + 1);
si = rs.s[rs.i];
rs.j = (rs.j + si);
sj = rs.s[rs.j];
rs.s[rs.i] = sj;
rs.s[rs.j] = si;
/* invalidate rs_buf */
rs->rs_have = 0;
memset(rsx->rs_buf, 0, sizeof(rsx->rs_buf));
return (rs.s[(si + sj) & 0xff]);
rs->rs_count = 1600000;
}
static inline uint32_t
arc4_getword(void)
static inline void
_rs_stir_if_needed(size_t len)
{
uint32_t val;
val = arc4_getbyte() << 24;
val |= arc4_getbyte() << 16;
val |= arc4_getbyte() << 8;
val |= arc4_getbyte();
return (val);
_rs_forkdetect();
if (!rs || rs->rs_count <= len)
_rs_stir();
if (rs->rs_count <= len)
rs->rs_count = 0;
else
rs->rs_count -= len;
}
static void
arc4_check_init(void)
static inline void
_rs_random_buf(void *_buf, size_t n)
{
if (!rs_initialized) {
arc4_init();
rs_initialized = 1;
u_char *buf = (u_char *)_buf;
u_char *keystream;
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
arc4_check_stir(void)
_rs_random_u32(uint32_t *val)
{
if (!rs_stired || arc4_count <= 0) {
arc4_stir();
rs_stired = 1;
}
u_char *keystream;
_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
arc4random_stir(void)
{
THREAD_LOCK();
arc4_check_init();
arc4_stir();
rs_stired = 1;
THREAD_UNLOCK();
_ARC4_LOCK();
_rs_stir();
_ARC4_UNLOCK();
}
void
arc4random_addrandom(u_char *dat, int datlen)
{
THREAD_LOCK();
arc4_check_init();
arc4_check_stir();
arc4_addrandom(dat, datlen);
THREAD_UNLOCK();
_ARC4_LOCK();
_rs_stir_if_needed(datlen);
_rs_rekey(dat, datlen);
_ARC4_UNLOCK();
}
uint32_t
arc4random(void)
{
uint32_t rnd;
THREAD_LOCK();
arc4_check_init();
arc4_check_stir();
rnd = arc4_getword();
arc4_count -= 4;
THREAD_UNLOCK();
uint32_t val;
return (rnd);
_ARC4_LOCK();
_rs_random_u32(&val);
_ARC4_UNLOCK();
return val;
}
void
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)
arc4random_buf(void *buf, size_t n)
{
const int iter = 1000000;
int i;
pctrval v;
v = rdtsc();
for (i = 0; i < iter; i++)
arc4random();
v = rdtsc() - v;
v /= iter;
printf("%qd cycles\n", v);
_ARC4_LOCK();
_rs_random_buf(buf, n);
_ARC4_UNLOCK();
}
#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);
}
_ARC4_ATFORK(_rs_forkhandler);
return (0);
}
/* $OpenBSD: arc4random_linux.h,v 1.8 2014/08/13 06:04:10 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)
#ifdef __GLIBC__
extern void *__dso_handle;
extern int __register_atfork(void (*)(void), void(*)(void), void (*)(void), void *);
#define _ARC4_ATFORK(f) __register_atfork(NULL, NULL, (f), __dso_handle)
#else
#define _ARC4_ATFORK(f) pthread_atfork(NULL, NULL, (f))
#endif
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