a85b5759f3
These files were pulled from the 1.6.3 release tarball. This new version builds against OpenSSL version 1.1 which will be the default in the new Debian Stable which is due to be released RealSoonNow (tm).
237 lines
5.2 KiB
C
237 lines
5.2 KiB
C
/* $OpenBSD: arc4random.c,v 1.41 2014/07/12 13:24:54 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>
|
|
*
|
|
* 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.
|
|
*/
|
|
#include "config.h"
|
|
|
|
/*
|
|
* ChaCha based random number generator for OpenBSD.
|
|
*/
|
|
|
|
#include <fcntl.h>
|
|
#include <limits.h>
|
|
#include <signal.h>
|
|
#ifdef HAVE_STDINT_H
|
|
#include <stdint.h>
|
|
#endif
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <sys/types.h>
|
|
#include <sys/param.h>
|
|
#include <sys/time.h>
|
|
#ifndef UB_ON_WINDOWS
|
|
#include <sys/mman.h>
|
|
#endif
|
|
|
|
#define KEYSTREAM_ONLY
|
|
#include "chacha_private.h"
|
|
|
|
#define arc4_min(a, b) ((a) < (b) ? (a) : (b))
|
|
#ifdef __GNUC__
|
|
#define inline __inline
|
|
#else /* !__GNUC__ */
|
|
#define inline
|
|
#endif /* !__GNUC__ */
|
|
#ifndef MAP_ANON
|
|
#define MAP_ANON MAP_ANONYMOUS
|
|
#endif
|
|
|
|
#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 {
|
|
size_t rs_have; /* valid bytes at end of rs_buf */
|
|
size_t rs_count; /* bytes till reseed */
|
|
} *rs;
|
|
|
|
/* Preserved in fork children. */
|
|
static struct {
|
|
chacha_ctx rs_chacha; /* chacha context for random keystream */
|
|
u_char rs_buf[RSBUFSZ]; /* keystream blocks */
|
|
} *rsx;
|
|
|
|
static inline void _rs_rekey(u_char *dat, size_t datlen);
|
|
|
|
static inline void
|
|
_rs_init(u_char *buf, size_t n)
|
|
{
|
|
if (n < KEYSZ + IVSZ)
|
|
return;
|
|
|
|
if (rs == NULL) {
|
|
#ifndef UB_ON_WINDOWS
|
|
if ((rs = mmap(NULL, sizeof(*rs), PROT_READ|PROT_WRITE,
|
|
MAP_ANON|MAP_PRIVATE, -1, 0)) == MAP_FAILED)
|
|
abort();
|
|
#ifdef MAP_INHERIT_ZERO
|
|
if (minherit(rs, sizeof(*rs), MAP_INHERIT_ZERO) == -1)
|
|
abort();
|
|
#endif
|
|
#else /* WINDOWS */
|
|
rs = malloc(sizeof(*rs));
|
|
if(!rs)
|
|
abort();
|
|
#endif
|
|
}
|
|
if (rsx == NULL) {
|
|
#ifndef UB_ON_WINDOWS
|
|
if ((rsx = mmap(NULL, sizeof(*rsx), PROT_READ|PROT_WRITE,
|
|
MAP_ANON|MAP_PRIVATE, -1, 0)) == MAP_FAILED)
|
|
abort();
|
|
#else /* WINDOWS */
|
|
rsx = malloc(sizeof(*rsx));
|
|
if(!rsx)
|
|
abort();
|
|
#endif
|
|
}
|
|
|
|
chacha_keysetup(&rsx->rs_chacha, buf, KEYSZ * 8, 0);
|
|
chacha_ivsetup(&rsx->rs_chacha, buf + KEYSZ);
|
|
}
|
|
|
|
static void
|
|
_rs_stir(void)
|
|
{
|
|
u_char rnd[KEYSZ + IVSZ];
|
|
|
|
if (getentropy(rnd, sizeof rnd) == -1) {
|
|
#ifdef SIGKILL
|
|
raise(SIGKILL);
|
|
#else
|
|
exit(9); /* windows */
|
|
#endif
|
|
}
|
|
|
|
if (!rs)
|
|
_rs_init(rnd, sizeof(rnd));
|
|
else
|
|
_rs_rekey(rnd, sizeof(rnd));
|
|
explicit_bzero(rnd, sizeof(rnd)); /* discard source seed */
|
|
|
|
/* invalidate rs_buf */
|
|
rs->rs_have = 0;
|
|
memset(rsx->rs_buf, 0, sizeof(rsx->rs_buf));
|
|
|
|
rs->rs_count = 1600000;
|
|
}
|
|
|
|
static inline void
|
|
_rs_stir_if_needed(size_t len)
|
|
{
|
|
#ifndef MAP_INHERIT_ZERO
|
|
static pid_t _rs_pid = 0;
|
|
pid_t pid = getpid();
|
|
|
|
/* If a system lacks MAP_INHERIT_ZERO, resort to getpid() */
|
|
if (_rs_pid == 0 || _rs_pid != pid) {
|
|
_rs_pid = pid;
|
|
if (rs)
|
|
rs->rs_count = 0;
|
|
}
|
|
#endif
|
|
if (!rs || rs->rs_count <= len)
|
|
_rs_stir();
|
|
if (rs->rs_count <= len)
|
|
rs->rs_count = 0;
|
|
else
|
|
rs->rs_count -= len;
|
|
}
|
|
|
|
static inline void
|
|
_rs_rekey(u_char *dat, size_t datlen)
|
|
{
|
|
#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 = arc4_min(datlen, KEYSZ + IVSZ);
|
|
for (i = 0; i < m; i++)
|
|
rsx->rs_buf[i] ^= dat[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 inline void
|
|
_rs_random_buf(void *_buf, size_t n)
|
|
{
|
|
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 = arc4_min(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
|
|
_rs_random_u32(uint32_t *val)
|
|
{
|
|
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);
|
|
}
|
|
|
|
uint32_t
|
|
arc4random(void)
|
|
{
|
|
uint32_t val;
|
|
|
|
_ARC4_LOCK();
|
|
_rs_random_u32(&val);
|
|
_ARC4_UNLOCK();
|
|
return val;
|
|
}
|
|
|
|
void
|
|
arc4random_buf(void *buf, size_t n)
|
|
{
|
|
_ARC4_LOCK();
|
|
_rs_random_buf(buf, n);
|
|
_ARC4_UNLOCK();
|
|
}
|