static const char __vcsid[] = "@(#) MirOS contributed arc4random.c (old)"
    "\n	@(#)rcsid_master: $MirOS: contrib/code/Snippets/arc4random.c,v 1.28 2010/09/12 12:24:27 tg Exp $"
    ;

/*-
 * Arc4 random number generator for OpenBSD.
 * Copyright 1996 David Mazieres <dm@lcs.mit.edu>.
 *
 * Modification and redistribution in source and binary forms is
 * permitted provided that due credit is given to the author and the
 * OpenBSD project by leaving this copyright notice intact.
 */

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

/*-
 * Modified by Robert Connolly from OpenBSD lib/libc/crypt/arc4random.c v1.11.
 * This is arc4random(3) using urandom.
 */

/*-
 * Copyright (c) 2008, 2009, 2010
 *	Thorsten Glaser <tg@mirbsd.org>
 * This is arc4random(3) made more portable,
 * as well as arc4random_pushb(3) for Cygwin.
 *
 * Provided that these terms and disclaimer and all copyright notices
 * are retained or reproduced in an accompanying document, permission
 * is granted to deal in this work without restriction, including un-
 * limited rights to use, publicly perform, distribute, sell, modify,
 * merge, give away, or sublicence.
 *
 * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
 * the utmost extent permitted by applicable law, neither express nor
 * implied; without malicious intent or gross negligence. In no event
 * may a licensor, author or contributor be held liable for indirect,
 * direct, other damage, loss, or other issues arising in any way out
 * of dealing in the work, even if advised of the possibility of such
 * damage or existence of a defect, except proven that it results out
 * of said person's immediate fault when using the work as intended.
 */

#include <sys/param.h>
#include <sys/types.h>
#include <sys/time.h>
#if defined(HAVE_SYS_SYSCTL_H) && HAVE_SYS_SYSCTL_H
#include <sys/sysctl.h>
#endif
#include <fcntl.h>
#if defined(HAVE_STDINT_H) && HAVE_STDINT_H
#include <stdint.h>
#elif defined(USE_INTTYPES)
#include <inttypes.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#if defined(__CYGWIN__) || defined(WIN32)
#define USE_MS_CRYPTOAPI
#define REDEF_USCORETYPES
#endif

#ifdef USE_MS_CRYPTOAPI
#define WIN32_WINNT 0x400
#define _WIN32_WINNT 0x400
#include <windows.h>
#include <wincrypt.h>

static uint8_t w32_buf[16*16384];	/* force reseed */
static uint8_t w32_hklm[80];		/* registry key (MS, Admin) */
static uint8_t w32_hkcu[256];		/* registry key (per user) */
static struct timeval w32_last;		/* last time CGR was used */

static char w32_subkey[] = "SOFTWARE\\Microsoft\\Cryptography\\RNG";
#endif

#ifndef MIN
#define	MIN(a,b)	(((a)<(b))?(a):(b))
#endif
#ifndef MAX
#define	MAX(a,b)	(((a)>(b))?(a):(b))
#endif

#ifdef REDEF_USCORETYPES
#define u_int32_t	uint32_t
#endif

#ifndef _PATH_URANDOM
#define _PATH_URANDOM	"/dev/urandom"
#endif

static struct arc4_stream {
	uint8_t i;
	uint8_t j;
	uint8_t s[256];
} arc4_ctx;

static int rs_initialized;
static pid_t arc4_stir_pid;
static int arc4_count;
static const char __randomdev[] = _PATH_URANDOM;

static uint8_t arc4_getbyte(void);
static void stir_finish(uint8_t);
static void arc4_atexit(void);
static char arc4_writeback(uint8_t *, size_t, char);

#ifndef arc4random_pushk
u_int32_t arc4random(void);
void arc4random_addrandom(u_char *, int);
void arc4random_stir(void);
#if defined(USE_MS_CRYPTOAPI) || defined(OPPORTUNISTIC_ROOT_PUSHB)
uint32_t arc4random_pushb(const void *, size_t);
#endif
#endif

#define NEED_UNIFORM_BUF_PROTO
#if defined(__OpenBSD__) && defined(OpenBSD) && (OpenBSD > 200805)
#undef NEED_UNIFORM_BUF_PROTO
#elif defined(__MirBSD__) && defined(MirBSD) && (MirBSD > 0x0AA4)
#undef NEED_UNIFORM_BUF_PROTO
#endif

#ifdef NEED_UNIFORM_BUF_PROTO
u_int32_t arc4random_uniform(u_int32_t);
void arc4random_buf(void *, size_t);
#endif

static void
arc4_init(void)
{
	int n;

	for (n = 0; n < 256; n++)
		arc4_ctx.s[n] = (uint8_t)n;
	arc4_ctx.i = 0;
	arc4_ctx.j = 0;
}

static void
arc4_addrandom(const u_char *dat, size_t datlen)
{
	size_t n = 0;
	uint8_t si;

	arc4_ctx.i--;
	while (n < 256) {
		arc4_ctx.i++;
		si = arc4_ctx.s[arc4_ctx.i];
		arc4_ctx.j = (uint8_t)(arc4_ctx.j + si + dat[n++ % datlen]);
		arc4_ctx.s[arc4_ctx.i] = arc4_ctx.s[arc4_ctx.j];
		arc4_ctx.s[arc4_ctx.j] = si;
	}
	arc4_ctx.i++;
	arc4_ctx.j = arc4_ctx.i;
}

#if defined(USE_MS_CRYPTOAPI)
#define RNDEV_BYTES	128
#elif defined(__INTERIX)
#define RNDEV_BYTES	4	/* slow /dev/urandom */
#elif defined(__OpenBSD__)
#define RNDEV_BYTES	(256 - (sizeof(struct timeval) + sizeof(pid_t)))
#elif defined(__CYGWIN__)
#define RNDEV_BYTES	64	/* /dev/urandom probably CryptoAPI */
#elif defined(__FreeBSD__)
#define RNDEV_BYTES	16	/* Yarrow has few state */
#elif defined(__GLIBC__)
#define RNDEV_BYTES	16	/* requested by maintainers */
#else
#define RNDEV_BYTES	8	/* unknown OS? */
#endif

static void
arc4_stir(void)
{
	int fd;
	struct {
		struct timeval tv;
		pid_t pid;
		u_int rnd[(RNDEV_BYTES + sizeof(u_int) - 1) / sizeof(u_int)];
	} rdat;
	size_t sz = 0;

	gettimeofday(&rdat.tv, NULL);
	rdat.pid = getpid();
	memcpy(rdat.rnd, __vcsid, MIN(sizeof(__vcsid), sizeof(rdat.rnd)));

#ifdef USE_MS_CRYPTOAPI
	if (arc4_writeback((char *)rdat.rnd, sizeof(rdat.rnd), 1))
		goto stir_okay;
#endif

	/* /dev/urandom is a multithread interface, sysctl is not. */
	/* Try to use /dev/urandom before sysctl. */
	fd = open(__randomdev, O_RDONLY);
	if (fd != -1) {
		sz = (size_t)read(fd, rdat.rnd, sizeof(rdat.rnd));
		close(fd);
	}
	if (sz > sizeof(rdat.rnd))
		sz = 0;
	if (fd == -1 || sz != sizeof(rdat.rnd)) {
		/* /dev/urandom failed? Maybe we're in a chroot. */
#if /* Linux */ defined(_LINUX_SYSCTL_H) || \
    /* OpenBSD */ (defined(CTL_KERN) && defined(KERN_ARND))
		int mib[3], nmib = 3;
		size_t i = sz / sizeof(u_int), len;

#ifdef _LINUX_SYSCTL_H
		mib[0] = CTL_KERN;
		mib[1] = KERN_RANDOM;
		mib[2] = RANDOM_UUID;
#else
		mib[0] = CTL_KERN;
		mib[1] = KERN_ARND;
		nmib = 2;
#endif

		while (i < sizeof(rdat.rnd) / sizeof(u_int)) {
			len = sizeof(u_int);
			if (sysctl(mib, nmib, &rdat.rnd[i++], &len,
			    NULL, 0) == -1) {
				fputs("warning: no entropy source\n", stderr);
				break;
			}
		}
#else
		/* XXX kFreeBSD doesn't seem to have KERN_ARND or so */
		;
#endif
	}

#ifdef USE_MS_CRYPTOAPI
 stir_okay:
#endif
	fd = arc4_getbyte();

	/*
	 * Time to give up. If no entropy could be found then we will just
	 * use gettimeofday and getpid.
	 */
	arc4_addrandom((u_char *)&rdat, sizeof(rdat));

	stir_finish(fd);
}

static void
stir_finish(uint8_t av)
{
	size_t n;
	uint8_t tb[16];

	arc4_stir_pid = getpid();

	/*
	 * Discard early keystream, as per recommendations in:
	 * http://www.wisdom.weizmann.ac.il/~itsik/RC4/Papers/Rc4_ksa.ps
	 * We discard 256 words. A long word is 4 bytes.
	 * We also discard a randomly fuzzed amount.
	 */
	n = 256 * 4 + (arc4_getbyte() & 0x0FU) + (av & 0xF0U);
	av &= 0x0FU;
	while (n--)
		arc4_getbyte();
	while (++n < sizeof(tb))
		tb[n] = arc4_getbyte();
	if (arc4_writeback(tb, sizeof(tb), 0))
		arc4_getbyte();
	while (av--)
		arc4_getbyte();
	arc4_count = 1600000;
}

static uint8_t
arc4_getbyte(void)
{
	uint8_t si, sj;

	arc4_ctx.i++;
	si = arc4_ctx.s[arc4_ctx.i];
	arc4_ctx.j = (uint8_t)(arc4_ctx.j + si);
	sj = arc4_ctx.s[arc4_ctx.j];
	arc4_ctx.s[arc4_ctx.i] = sj;
	arc4_ctx.s[arc4_ctx.j] = si;
	return (arc4_ctx.s[(si + sj) & 0xff]);
}

static uint32_t
arc4_getword(void)
{
	uint32_t val;
	val = (uint32_t)arc4_getbyte() << 24;
	val |= (uint32_t)arc4_getbyte() << 16;
	val |= (uint32_t)arc4_getbyte() << 8;
	val |= (uint32_t)arc4_getbyte();
	return (val);
}

void
arc4random_stir(void)
{
	if (!rs_initialized) {
		arc4_init();
		rs_initialized = 1;
		atexit(arc4_atexit);
	}
	arc4_stir();
}

void
arc4random_addrandom(u_char *dat, int datlen)
{
	if (!rs_initialized)
		arc4random_stir();
	arc4_addrandom(dat, datlen);
}

u_int32_t
arc4random(void)
{
	arc4_count -= 4;
	if (arc4_count <= 0 || !rs_initialized || arc4_stir_pid != getpid())
		arc4random_stir();
	return arc4_getword();
}

/*
 * Returns 0 if write error; 0 if do_rd and read error;
 * 1 if !do_rd and read error but not write error;
 * 1 if no error occured.
 */
static char
arc4_writeback(uint8_t *buf, size_t len, char do_rd)
{
#ifdef USE_MS_CRYPTOAPI
	static char has_provider = 0;
	static HCRYPTPROV p;
	HKEY hKeyLM, hKeyCU;
	DWORD ksz;
	char rc = 6, has_rkey = 0, w32_a4b[16];
	size_t i, j, xlen;
	struct timeval tv;

	for (i = 0; i < sizeof(w32_a4b); ++i)
		w32_a4b[i] = arc4_getbyte();
	for (i = arc4_getbyte() & 15; i; --i)
		arc4_getbyte();

	ksz = sizeof(w32_buf);
	if ((/* read-write */ RegOpenKeyEx(HKEY_LOCAL_MACHINE, w32_subkey,
	    0, KEY_QUERY_VALUE | KEY_SET_VALUE, &hKeyLM) == ERROR_SUCCESS ||
	    /* try read-only */ RegOpenKeyEx(HKEY_LOCAL_MACHINE, w32_subkey,
	    0, KEY_QUERY_VALUE, &hKeyLM) == ERROR_SUCCESS) && /* get value */
	    (RegQueryValueEx(hKeyLM, "Seed", NULL, NULL, w32_buf, &ksz) ==
	    ERROR_SUCCESS) && /* got any content? */ ksz) {
		/* we got HKLM key, read-write or read-only */
		has_rkey |= 1;
		/* move content to destination */
		memset(w32_hklm, '\0', sizeof(w32_hklm));
		for (i = 0; i < MAX(ksz, sizeof(w32_hklm)); ++i)
			w32_hklm[i % sizeof(w32_hklm)] ^= w32_buf[i % ksz];
	}
	ksz = sizeof(w32_buf);
	if ((/* read-write */ RegCreateKeyEx(HKEY_CURRENT_USER, w32_subkey,
	    0, NULL, 0, KEY_QUERY_VALUE | KEY_SET_VALUE, NULL, &hKeyCU,
	    NULL) == ERROR_SUCCESS || /* R/O */ RegOpenKeyEx(HKEY_CURRENT_USER,
	    w32_subkey, 0, KEY_QUERY_VALUE, &hKeyLM) == ERROR_SUCCESS) &&
	    /* get value */ (RegQueryValueEx(hKeyLM, "Seed", NULL, NULL,
	    w32_buf, &ksz) == ERROR_SUCCESS) && /* got any content? */ ksz) {
		/* we got HKCU key, created, read-write or read-only */
		has_rkey |= 2;
		/* move content to destination */
		memset(w32_hkcu, '\0', sizeof(w32_hkcu));
		for (i = 0; i < MAX(ksz, sizeof(w32_hkcu)); ++i)
			w32_hkcu[i % sizeof(w32_hkcu)] ^= w32_buf[i % ksz];
	}

	if (!do_rd)
		goto nogen_out;

	if (has_rkey && gettimeofday(&tv, NULL) == 0) {
		/* we have registry key; rate-limit CryptGenRandom */
		if (tv.tv_sec - w32_last.tv_sec < 128 + (arc4_getbyte() & 127))
			goto nogen_out;
		/* nope, more than 2-4 minutes, call it */
		w32_last.tv_sec = tv.tv_sec;
	}

	if (!has_provider) {
		if (!CryptAcquireContext(&p, NULL, NULL, PROV_RSA_FULL, 0)) {
			if ((HRESULT)GetLastError() != NTE_BAD_KEYSET)
				goto nogen_out;
			if (!CryptAcquireContext(&p, NULL, NULL, PROV_RSA_FULL,
			    CRYPT_NEWKEYSET))
				goto nogen_out;
		}
		has_provider = 1;
	}
	i = 0;
	while (i < 256)
		w32_buf[i++] = arc4_getbyte();
	if (!CryptGenRandom(p, sizeof(w32_buf), w32_buf)) {
		w32_last.tv_sec = 0;
 nogen_out:
		rc |= 1;
		memset(w32_buf, '\0', 256);
	}
	xlen = MIN(sizeof(w32_buf) - sizeof(w32_hklm) - sizeof(w32_hkcu) -
	    sizeof(w32_a4b), len);
	j = xlen + sizeof(w32_hklm) + sizeof(w32_hkcu) + sizeof(w32_a4b);
	for (i = 0; i < MAX(j, len); ++i)
		w32_buf[i % j] ^= w32_hklm[i % sizeof(w32_hklm)] ^
		    w32_hkcu[i % sizeof(w32_hkcu)] ^ buf[i % len] ^
		    arc4_getbyte();
	if (has_rkey & 1) {
		if (RegSetValueEx(hKeyLM, "Seed", 0, REG_BINARY,
		    w32_buf, sizeof(w32_hklm)) == ERROR_SUCCESS)
			rc &= ~2;
		RegCloseKey(hKeyLM);
	}
	if (has_rkey & 2) {
		if (RegSetValueEx(hKeyCU, "Seed", 0, REG_BINARY,
		    w32_buf + sizeof(w32_hklm), sizeof(w32_hkcu)) ==
		    ERROR_SUCCESS)
			rc &= ~4;
		RegCloseKey(hKeyCU);
	}
	for (i = 0; i < sizeof(w32_a4b); ++i)
		w32_a4b[i] ^= w32_buf[sizeof(w32_hklm) + sizeof(w32_hkcu) + i];
	arc4_addrandom(w32_a4b, sizeof(w32_a4b));

	i = sizeof(w32_hklm) + sizeof(w32_hkcu) + sizeof(w32_a4b);
	while (len) {
		j = MIN(len, xlen);
		memcpy(buf, w32_buf + i, j);
		buf += j;
		len -= j;
	}

	memset(w32_buf, '\0', sizeof(w32_buf));

	return (
	    /* read error occured */
	    (!has_rkey && (rc & 1)) ? 0 :
	    /* don't care about write errors */
	    !do_rd ? 1 :
	    /* couldn't write */
	    (rc & 6) == 6 ? 0 :
	    /* at least one RegSetValueEx succeeded */
	    1);
#elif defined(arc4random_pushk)
	uint32_t num;

	num = arc4random_pushk(buf, len);
	memcpy(buf, &num, sizeof(num));
	return (do_rd ? 0 : 1);
#else
	int fd;

	if ((fd = open(__randomdev, O_WRONLY)) != -1) {
		if (write(fd, buf, len) < 4)
			do_rd = 1;
		close(fd);
	}
	return (do_rd || fd == -1 ? 0 : 1);
#endif
}

#if defined(USE_MS_CRYPTOAPI) || defined(arc4random_pushk) || \
    defined(OPPORTUNISTIC_ROOT_PUSHB)
uint32_t
arc4random_pushb(const void *src, size_t len)
{
	size_t rlen;
	union {
		uint8_t buf[256];
		struct {
			struct timeval tv;
			const void *sp, *dp;
			size_t sz;
			uint32_t vu;
		} s;
		uint32_t xbuf;
	} idat;
	uint32_t res = 1;

	if (!rs_initialized) {
		arc4_init();
		rs_initialized = 1;
	}

	idat.s.sp = &idat;
	idat.s.dp = src;
	idat.s.sz = len;
	idat.s.vu = arc4_getword();
	gettimeofday(&idat.s.tv, NULL);

	rlen = MAX(sizeof(idat.s), len);
	while (rlen--)
		idat.buf[rlen % sizeof(idat.buf)] ^=
		    ((const uint8_t *)src)[rlen % len];
	rlen = MIN(sizeof(idat), MAX(sizeof(idat.s), len));

	if (arc4_writeback((void *)&idat, rlen, 1))
		res = 0;
	arc4_addrandom((void *)&idat, rlen);
	rlen = arc4_getbyte() & 1;
	if (res)
		res = idat.xbuf;
	else
		/* we got entropy from the kernel, so consider us stirred */
		stir_finish(idat.buf[5]);
	if (rlen)
		(void)arc4_getbyte();
	return (res ^ arc4_getword());
}
#endif

static void
arc4_atexit(void)
{
	struct {
		pid_t spid;
		int cnt;
		uint8_t carr[240];
	} buf;
	int i = 0;

	while (i < 240)
		buf.carr[i++] = arc4_getbyte();
	buf.spid = arc4_stir_pid;
	buf.cnt = arc4_count;

	arc4_writeback((uint8_t *)&buf, sizeof(buf), 0);
}

void
arc4random_buf(void *_buf, size_t n)
{
	uint8_t *buf = (uint8_t *)_buf;

	if (!rs_initialized || arc4_stir_pid != getpid())
		arc4random_stir();
	buf[0] = arc4_getbyte() % 3;
	while (buf[0]--)
		(void)arc4_getbyte();
	while (n--) {
		if (--arc4_count <= 0)
			arc4_stir();
		buf[n] = arc4_getbyte();
	}
}

/*-
 * Written by Damien Miller.
 * With simplifications by Jinmei Tatuya.
 */

/*
 * 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 the range
 * [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 defined(ULONG_MAX) && (ULONG_MAX > 0xFFFFFFFFUL)
	min = 0x100000000UL % upper_bound;
#else
	/* calculate (2^32 % upper_bound) avoiding 64-bit math */
	if (upper_bound > 0x80000000U)
		/* 2^32 - upper_bound (only one "value area") */
		min = 1 + ~upper_bound;
	else
		/* ((2^32 - x) % x) == (2^32 % x) when x <= 2^31 */
		min = (0xFFFFFFFFU - upper_bound + 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 (at all).
	 */
	arc4_count -= 4;
	if (!rs_initialized || arc4_stir_pid != getpid() || arc4_count <= 0)
		arc4random_stir();
	if (arc4_getbyte() & 1)
		(void)arc4_getbyte();
	do {
		r = arc4_getword();
	} while (r < min);

	return (r % upper_bound);
}
