/*
 * Copyright (c) 2008 Christian Biere
 * Copyright (c) 2008, 2012, 2014-2015 Raphael Manfredi
 *
 *----------------------------------------------------------------------
 * This file is part of gtk-gnutella.
 *
 *  gtk-gnutella is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  gtk-gnutella is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with gtk-gnutella; if not, write to the Free Software
 *  Foundation, Inc.:
 *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 *----------------------------------------------------------------------
 */

/**
 * @ingroup lib
 * @file
 *
 * Entropy collection.
 *
 * The aim is to produce random bits by probing the environment and combining
 * the information collected with a good hash function (SHA-1 here) to create
 * sufficient diffusion.
 *
 * The entropy collected here is meant to be as unique as possible, even
 * when called multiple times (with the same items being probed), so that
 * more than 160 bits of randomness can be collected.  Ensuring a good initial
 * random seed is critical when generating unique IDs, such as a servent GUID
 * or a DHT identifier.
 *
 * In order to do that, a simple pseudo-random number generation (PRNG) engine
 * is used to vary the order with which each source of "randomness" is probed.
 * An history of the collected entropy is also kept through successive merging
 * of newly collected bits with previously collected ones.
 *
 * Entropy is mostly collected at the beginning to initialize some random
 * values and set the initial state of much stronger PRNG engines: ARC4,
 * WELL, or MT.
 *
 * When AJE (Alea Jacta Est) has been initialized, all entropy retrieval is
 * transparently remapped to the global AJE state.
 *
 * Why so much care at computing randomness?
 *
 * The aim here is not to achieve cryptographic strength but rather make
 * sure the random sequence generated by this program will be as unique
 * as possible over time, and as unpredictable as possible.
 *
 * Uniqueness takes care of random IDs that need to be unique, even if millions
 * of CPUs generate such an ID at the same time using the same exact logic.
 *
 * Unpredictability takes care of possible hostility against the random
 * sequence, whereby an attacker would attempt to generate similar random
 * IDs or attempt to guess what the next ID will be based on recently seen
 * random IDs.  Being able to anticipate the next IDs would be the basis of
 * denial of service.
 *
 * Good randomness requires the use of good pseudo random number generators
 * (PRNGs), proper initial seeding and understanding the limits imposed by the
 * (necessarily constrained) size of the internal context of these PRNGs.
 *
 * Strong randomness requires the use of cryptographically secure PRNGs, so
 * that prediction of future outputs based on previous inputs cannot be easily
 * made (meaning the internal context remains unknown despite the output of
 * the algorithm being seen).
 *
 * @author Christian Biere
 * @date 2008
 * @author Raphael Manfredi
 * @date 2008, 2012, 2014-2015
 */

#include "common.h"

#ifdef I_PWD
#include <pwd.h>				/* For getpwuid() and struct passwd */
#endif

#include "entropy.h"

#include "aje.h"
#include "atomic.h"
#include "bigint.h"
#include "compat_misc.h"
#include "compat_statvfs.h"
#include "compat_usleep.h"
#include "crc.h"
#include "endian.h"
#include "getgateway.h"
#include "gethomedir.h"
#include "halloc.h"				/* For halloc_stats_digest() */
#include "hashing.h"
#include "host_addr.h"
#include "log.h"
#include "mempcpy.h"
#include "misc.h"
#include "palloc.h"				/* For palloc_stats_digest() */
#include "pow2.h"
#include "pslist.h"
#include "rand31.h"
#include "random.h"
#include "sha1.h"
#include "shuffle.h"
#include "spinlock.h"
#include "stringify.h"
#include "thread.h"
#include "tm.h"
#include "tmalloc.h"			/* For tmalloc_stats_digest() */
#include "unsigned.h"
#include "vmm.h"				/* For vmm_trap_page() */
#include "xmalloc.h"			/* For xmalloc_stats_digest() */
#include "zalloc.h"				/* For zalloc_stats_digest() */

#include "override.h"			/* Must be the last header included */

/**
 * Maximum amount of items we can randomly shuffle.
 *
 * We're using a PRNG with 128 bits of internal context and a period of, at
 * least, 2**125.  To ensure we can reach all the possible permutations of
 * the set, we cannot shuffle more than 33 items since 33! < 2**123 but 34!
 * is greater than the minimum period.
 */
#define RANDOM_SHUFFLE_MAX	33		/* 33! < 2**123 */

typedef void (*entropy_cb_t)(SHA1_context *ctx);

/**
 * Buffer where we keep track of previously generated randomness.
 */
static sha1_t entropy_previous;
static spinlock_t entropy_previous_slk = SPINLOCK_INIT;

#define ENTROPY_PREV_LOCK		spinlock_hidden(&entropy_previous_slk)
#define ENTROPY_PREV_UNLOCK		spinunlock_hidden(&entropy_previous_slk)

#define ENTROPY_NONCE_MAX	1023	/* Change "nonce" base after that many uses */

/**
 * Context for the entropy_minirand() routine.
 */
static struct entropy_minictx {
	spinlock_t lock;
	uint32 x, y, z, c;
	bool seeded;
} entropy_minictx = {
	SPINLOCK_INIT,
	0, 0, 0, 0,
	FALSE
};
#define ENTROPY_MINICTX_LOCK(c)		spinlock_hidden(&(c)->lock)
#define ENTROPY_MINICTX_UNLOCK(c)	spinunlock_hidden(&(c)->lock)

static void entropy_seed(struct entropy_minictx *c);

static void
sha1_feed_ulong(SHA1_context *ctx, unsigned long value)
{
	SHA1_INPUT(ctx, value);
}

static void
sha1_feed_uint(SHA1_context *ctx, unsigned value)
{
	SHA1_INPUT(ctx, value);
}

static void
sha1_feed_double(SHA1_context *ctx, double value)
{
	SHA1_INPUT(ctx, value);
}

static void
sha1_feed_pointer(SHA1_context *ctx, const void *p)
{
	SHA1_INPUT(ctx, p);
}

static void
sha1_feed_string(SHA1_context *ctx, const char *s)
{
	sha1_feed_pointer(ctx, s);
	if (s != NULL) {
		SHA1_input(ctx, s, vstrlen(s));
	}
}

static void
sha1_feed_errno(SHA1_context *ctx)
{
	uint32 e = integer_hash_fast(errno);
	uint32 b = rand31_u32() & 0x1f;
	tm_nano_t now;

	tm_precise_time(&now);
	sha1_feed_uint(ctx, integer_hash_fast(now.tv_nsec) + UINT32_ROTL(e, b));
}

static void
sha1_feed_stat(SHA1_context *ctx, const char *path)
{
	filestat_t buf;

	sha1_feed_string(ctx, path);

	if (-1 != stat(path, &buf)) {
		SHA1_INPUT(ctx, buf);
	} else {
		sha1_feed_errno(ctx);
	}
}

static void
sha1_feed_fstat(SHA1_context *ctx, int fd)
{
	filestat_t buf;

	sha1_feed_uint(ctx, fd);

	if (-1 != fstat(fd, &buf)) {
		SHA1_INPUT(ctx, buf);
	} else {
		sha1_feed_errno(ctx);
	}
}

static void
sha1_feed_statvfs(SHA1_context *ctx, const char *path)
{
	struct statvfs buf;

	sha1_feed_string(ctx, path);

	if (-1 != compat_statvfs(path, &buf)) {
		SHA1_INPUT(ctx, buf);
	} else {
		sha1_feed_errno(ctx);
	}
}

/**
 * Create a small but unpredictable delay in the process execution.
 */
void
entropy_delay(void)
{
	thread_yield();
}

/**
 * Add entropy from previous calls.
 */
static void G_COLD
entropy_merge(sha1_t *digest)
{
	bigint_t older, newer;

	STATIC_ASSERT(sizeof entropy_previous == SHA1_RAW_SIZE);

	/*
	 * These big integers operate on the buffer space from ``digest'' and
	 * ``entropy_previous'' directly.
	 */

	ENTROPY_PREV_LOCK;

	bigint_use(&older, VARLEN(entropy_previous));
	bigint_use(&newer, PTRLEN(digest));
	bigint_add(&newer, &older);
	bigint_copy(&older, &newer);

	ENTROPY_PREV_UNLOCK;
}

#define ENTROPY_KISS_MULT	698769069L

/**
 * Minimal random number generation, to be used very early in the process
 * initialization when we cannot use entropy_minimal_collect() yet.
 *
 * @note
 * This routine MUST NOT be used directly by applications, as it is only
 * meant to be used in entropy_array_shuffle() for internal shuffling purposes
 * and in other parts of the entropy collection process where we need a random
 * number but have not finished collecting entropy yet.
 * It is only exported to be exercised in the random-test program.
 *
 * @return a 32-bit random number.
 */
uint32
entropy_minirand(void)
{
	uint64 t;
	uint32 r;
	struct entropy_minictx *ctx = &entropy_minictx;

	ENTROPY_MINICTX_LOCK(ctx);

	if G_UNLIKELY(!ctx->seeded) {
		ctx->seeded = TRUE;
		entropy_seed(ctx);
	}

	/*
	 * George Marsaglia's KISS alogorithm, posted in sci.math circa 2003.
	 * The period of this PRNG is more than 2**125, and it keeps 128 bits
	 * of context.
	 */

	ctx->x = 69069 * ctx->x + 12345;
	ctx->y ^= (ctx->y << 13);
	ctx->y ^= (ctx->y >> 17);
	ctx->y ^= (ctx->y << 5);
	t = (uint64) ENTROPY_KISS_MULT * ctx->z + ctx->c;
	ctx->c = t >> 32;
	r = ctx->x + ctx->y + (ctx->z = t);

	ENTROPY_MINICTX_UNLOCK(ctx);

	return r;
}

/**
 * Shuffle array in-place.
 */
static void
entropy_array_shuffle(void *ary, size_t len, size_t elem_size)
{
	g_assert(ary != NULL);
	g_assert(size_is_non_negative(len));
	g_assert(size_is_positive(elem_size));

	if (len > RANDOM_SHUFFLE_MAX)
		s_carp("%s: cannot shuffle %zu items without bias", G_STRFUNC, len);

	shuffle_with(entropy_minirand, ary, len, elem_size);
}

/**
 * Collect entropy by randomly executing the callbacks given in the array.
 */
static void
entropy_array_cb_collect(SHA1_context *ctx, entropy_cb_t *ary, size_t len)
{
	size_t i;

	g_assert(ctx != NULL);
	g_assert(ary != NULL);
	g_assert(size_is_non_negative(len));

	entropy_array_shuffle(ary, len, sizeof ary[0]);

	for (i = 0; i < len; i++) {
		(*ary[i])(ctx);
	}
}

enum entropy_data {
	ENTROPY_SHA1,
	ENTROPY_ULONG,
	ENTROPY_STRING,
	ENTROPY_STAT,
	ENTROPY_FSTAT,
	ENTROPY_STATVFS,
	ENTROPY_DOUBLE,
	ENTROPY_POINTER
};

/**
 * Collect entropy by randomly feeding values from array.
 */
static void
entropy_array_data_collect(SHA1_context *ctx,
	enum entropy_data data, void *ary, size_t len, size_t elem_size)
{
	size_t i;
	void *p;

	g_assert(ctx != NULL);
	g_assert(ary != NULL);
	g_assert(size_is_non_negative(len));
	g_assert(size_is_positive(elem_size));

	entropy_array_shuffle(ary, len, elem_size);

	for (i = 0, p = ary; i < len; i++, p = ptr_add_offset(p, elem_size)) {
		switch (data) {
		case ENTROPY_SHA1:
			SHA1_input(ctx, p, SHA1_RAW_SIZE);
			break;
		case ENTROPY_ULONG:
			sha1_feed_ulong(ctx, *(unsigned long *) p);
			break;
		case ENTROPY_STRING:
			sha1_feed_string(ctx, *(char **) p);
			break;
		case ENTROPY_STAT:
			sha1_feed_stat(ctx, *(char **) p);
			break;
		case ENTROPY_FSTAT:
			sha1_feed_fstat(ctx, *(int *) p);
			break;
		case ENTROPY_STATVFS:
			sha1_feed_statvfs(ctx, *(char **) p);
			break;
		case ENTROPY_DOUBLE:
			sha1_feed_double(ctx, *(double *) p);
			break;
		case ENTROPY_POINTER:
			sha1_feed_pointer(ctx, *(void **) p);
			break;
		}
	}
}

/**
 * Collect entropy by randomly feeding unsigned long values from array.
 */
static void
entropy_array_ulong_collect(SHA1_context *ctx, unsigned long *ary, size_t len)
{
	entropy_array_data_collect(ctx, ENTROPY_ULONG, ary, len, sizeof ary[0]);
}

/**
 * Collect entropy by randomly feeding strings from array.
 */
static void
entropy_array_string_collect(SHA1_context *ctx, const char **ary, size_t len)
{
	entropy_array_data_collect(ctx, ENTROPY_STRING, ary, len, sizeof ary[0]);
}

/**
 * Collect entropy by randomly feeding stat() info from paths in array.
 */
static void
entropy_array_stat_collect(SHA1_context *ctx, const char **ary, size_t len)
{
	entropy_array_data_collect(ctx, ENTROPY_STAT, ary, len, sizeof ary[0]);
}

/**
 * Collect entropy by randomly feeding statvfs() info from paths in array.
 */
static void
entropy_array_statvfs_collect(SHA1_context *ctx, const char **ary, size_t len)
{
	entropy_array_data_collect(ctx, ENTROPY_STATVFS, ary, len, sizeof ary[0]);
}

/**
 * Collect entropy by randomly feeding fstat() info from file descriptors.
 */
static void
entropy_array_fstat_collect(SHA1_context *ctx, int *ary, size_t len)
{
	entropy_array_data_collect(ctx, ENTROPY_FSTAT, ary, len, sizeof ary[0]);
}

/**
 * Collect entropy by randomly feeding double values from array.
 */
static void
entropy_array_double_collect(SHA1_context *ctx, double *ary, size_t len)
{
	entropy_array_data_collect(ctx, ENTROPY_DOUBLE, ary, len, sizeof ary[0]);
}

/**
 * Collect entropy by randomly feeding pointers from array.
 */
static void
entropy_array_pointer_collect(SHA1_context *ctx, void **ary, size_t len)
{
	entropy_array_data_collect(ctx, ENTROPY_POINTER, ary, len, sizeof ary[0]);
}

/**
 * Collect hopefully random bytes.
 */
static void
entropy_collect_randomness(SHA1_context *ctx)
{
#ifdef MINGW32
	{
		uint8 data[128];
		if (0 == mingw_random_bytes(VARLEN(data))) {
			s_warning("%s(): unable to generate random bytes: %m", G_STRFUNC);
		} else {
			SHA1_INPUT(ctx, data);
		}
	}
#else	/* !MINGW32 */
	{
		filestat_t buf;
		FILE *f = NULL;
		bool is_pipe = TRUE;

		/*
		 * If we have a /dev/urandom character device, use it.
		 * Otherwise, launch ps and grab its output.
		 */

		if (-1 != stat("/dev/urandom", &buf) && S_ISCHR(buf.st_mode)) {
			f = fopen("/dev/urandom", "r");
			is_pipe = FALSE;
			SHA1_INPUT(ctx, buf);
		} else if (-1 != access("/bin/ps", X_OK)) {
			f = popen("/bin/ps -ef", "r");
		} else if (-1 != access("/usr/bin/ps", X_OK)) {
			f = popen("/usr/bin/ps -ef", "r");
		} else if (-1 != access("/usr/ucb/ps", X_OK)) {
			f = popen("/usr/ucb/ps aux", "r");
		}

		if (f == NULL) {
			s_warning("%s(): was unable to %s on your system",
				G_STRFUNC,
				is_pipe ? "find the ps command" : "open /dev/urandom");
		} else {
			/*
			 * Compute the SHA1 of the output (either ps or /dev/urandom).
			 */

			for (;;) {
				uint8 data[1024];
				size_t r, len = sizeof(data);

				r = fread(data, 1, len, f);
				if (r > 0)
					SHA1_input(ctx, data, r);
				if (r < len || !is_pipe)	/* Read once from /dev/urandom */
					break;
			}

			if (is_pipe)
				pclose(f);
			else
				fclose(f);
		}
	}
#endif	/* MINGW32 */
}

/**
 * Collect user ID information.
 */
static void
entropy_collect_user_id(SHA1_context *ctx)
{
	unsigned long id[2];

#ifdef HAS_GETUID
	id[0] = getuid();
	id[1] = getgid();
#else
	id[0] = entropy_minirand();
	id[1] = entropy_minirand();
#endif	/* HAS_GETUID */

	entropy_array_ulong_collect(ctx, id, N_ITEMS(id));
}

/**
 * Collect process ID information
 */
static void
entropy_collect_process_id(SHA1_context *ctx)
{
	unsigned long id[2];

	id[0] = getppid();
	id[1] = getpid();

	entropy_array_ulong_collect(ctx, id, N_ITEMS(id));
}

/**
 * Collect compile-time information.
 */
static void
entropy_collect_compile_time(SHA1_context *ctx)
{
	const char *str[2];

	str[0] = __DATE__;
	str[1] = __TIME__;

	entropy_array_string_collect(ctx, str, N_ITEMS(str));
}

/**
 * Collect user information.
 */
static void
entropy_collect_user(SHA1_context *ctx)
{
	const char *str[3];

	str[0] = gethomedir();

#if GLIB_CHECK_VERSION(2,6,0)
	/*
	 * These functions cannot be used with an unpatched GLib 1.2 on some
	 * systems as they trigger a bug in GLib causing a crash.  On Darwin
	 * there's still a problem before GLib 2.6 due to a bug in Darwin though.
	 */

	str[1] = g_get_user_name();
	str[2] = g_get_real_name();
	entropy_array_string_collect(ctx, str, N_ITEMS(str));
#else
	{
		char user[UINT32_DEC_BUFLEN];
		char real[UINT32_DEC_BUFLEN];

		uint32_to_string_buf(entropy_minirand(), ARYLEN(user));
		uint32_to_string_buf(entropy_minirand(), ARYLEN(real));
		str[1] = user;
		str[2] = real;
		entropy_array_string_collect(ctx, str, N_ITEMS(str));
	}
#endif	/* GLib >= 2.0 */
}

/**
 * Collect login information.
 */
static void
entropy_collect_login(SHA1_context *ctx)
{
#ifdef HAS_GETLOGIN
	{
		const char *name = getlogin();
		sha1_feed_string(ctx, name);
		sha1_feed_pointer(ctx, name);	/* name points to static data */
	}
#else
	sha1_feed_uint(ctx, entropy_minirand());
#endif	/* HAS_GETLOGIN */
}

/**
 * Collect information from /etc/passwd.
 */
static void
entropy_collect_pw(SHA1_context *ctx)
{
#ifdef HAS_GETPWUID
	{
		const struct passwd *pp = getpwuid(getuid());

		sha1_feed_pointer(ctx, pp);	/* pp points to static data */
		if (pp != NULL) {
			SHA1_INPUT(ctx, *pp);
		} else {
			sha1_feed_errno(ctx);
		}
	}
#else
	sha1_feed_uint(ctx, entropy_minirand());
#endif	/* HAS_GETPWUID */
}

/**
 * Collect information from file system.
 */
static void
entropy_collect_filesystem(SHA1_context *ctx)
{
	const char *path[RANDOM_SHUFFLE_MAX];
	size_t i = 0;

	path[i++] = gethomedir();
	path[i++] = ".";
	path[i++] = "..";
	path[i++] = "../..";
	path[i++] = "../../..";
	path[i++] = "/";

	if (is_running_on_mingw()) {
		path[i++] = "C:/";
		path[i++] = "C:/WINDOWS/Temp";
		path[i++] = mingw_get_admin_tools_path();
		path[i++] = mingw_get_common_appdata_path();
		path[i++] = mingw_get_common_docs_path();
		path[i++] = mingw_get_cookies_path();
		path[i++] = mingw_get_fonts_path();
		path[i++] = mingw_get_history_path();
		path[i++] = mingw_get_home_path();
		path[i++] = mingw_get_internet_cache_path();
		path[i++] = mingw_get_mypictures_path();
		path[i++] = mingw_get_personal_path();
		path[i++] = mingw_get_program_files_path();
		path[i++] = mingw_get_startup_path();
		path[i++] = mingw_get_system_path();
		path[i++] = mingw_get_windows_path();
	} else {
		path[i++] = "/bin";
		path[i++] = "/boot";
		path[i++] = "/dev";
		path[i++] = "/etc";
		path[i++] = "/home";
		path[i++] = "/lib";
		path[i++] = "/mnt";
		path[i++] = "/opt";
		path[i++] = "/proc";
		path[i++] = "/root";
		path[i++] = "/sbin";
		path[i++] = "/sys";
		path[i++] = "/tmp";
		path[i++] = "/usr";
		path[i++] = "/var";
		path[i++] = "/var/run";
	}

	g_assert(i <= N_ITEMS(path));

	entropy_array_stat_collect(ctx, path, i);
	entropy_array_statvfs_collect(ctx, path, i);
}

/**
 * Collect entropy from standard file descriptors.
 */
static void
entropy_collect_stdio(SHA1_context *ctx)
{
	int fd[3];

	fd[0] = STDIN_FILENO;
	fd[1] = STDOUT_FILENO;
	fd[2] = STDERR_FILENO;

	entropy_array_fstat_collect(ctx, fd, N_ITEMS(fd));
}

/**
 * Collect entropy from available space on filesystem.
 */
static void
entropy_collect_free_space(SHA1_context *ctx)
{
	double fs[3];

	fs[0] = fs_free_space_pct(gethomedir());
	fs[1] = fs_free_space_pct("/");
	fs[2] = fs_free_space_pct(".");

	entropy_array_double_collect(ctx, fs, N_ITEMS(fs));
}

/**
 * Collect entropy from used CPU time.
 */
static void
entropy_collect_usage(SHA1_context *ctx)
{
#ifdef HAS_GETRUSAGE
	{
		struct rusage usage;

		if (-1 != getrusage(RUSAGE_SELF, &usage)) {
			SHA1_INPUT(ctx, usage);
		} else {
			sha1_feed_errno(ctx);
		}
	}
#else
	sha1_feed_uint(ctx, entropy_minirand());
#endif	/* HAS_GETRUSAGE */
}

/**
 * Collect entropy from system name.
 */
static void
entropy_collect_uname(SHA1_context *ctx)
{
#ifdef HAS_UNAME
	{
		struct utsname un;

		if (-1 != uname(&un)) {
			SHA1_INPUT(ctx, un);
		} else {
			sha1_feed_errno(ctx);
		}
	}
#else
	sha1_feed_uint(ctx, entropy_minirand());
#endif	/* HAS_UNAME */
}

/**
 * Collect entropy from terminal line name.
 */
static void
entropy_collect_ttyname(SHA1_context *ctx)
{
#ifdef HAS_TTYNAME
	sha1_feed_string(ctx, ttyname(STDIN_FILENO));
#else
	sha1_feed_uint(ctx, entropy_minirand());
#endif	/* HAS_TTYNAME */
}

/**
 * Collect entropy from amount of files we can open.
 */
static void
entropy_collect_file_amount(SHA1_context *ctx)
{
	sha1_feed_uint(ctx, getdtablesize());
}

/**
 * Collect entropy from constant pointers.
 */
static void
entropy_collect_pointers(SHA1_context *ctx)
{
	void *ptr[6];

	ptr[0] = ctx;
	ptr[1] = cast_func_to_pointer(&entropy_collect);
	ptr[2] = cast_func_to_pointer(&entropy_collect_pointers);
	ptr[3] = cast_func_to_pointer(&exit);	/* libc */
	ptr[4] = &errno;
	ptr[5] = &ptr;

	entropy_array_pointer_collect(ctx, ptr, N_ITEMS(ptr));
}

/**
 * Collect entropy based on current CPU state.
 */
static void
entropy_collect_cpu(SHA1_context *ctx)
{
	jmp_buf env;
	ulong r[sizeof(env) / (sizeof(ulong))];		/* extra () to silence clang */

	/*
	 * Add local CPU state noise.
	 */

	ZERO(&env);			/* Avoid uninitialized memory reads */

	if (Setjmp(env)) {
		/* We will never longjmp() back here */
		g_assert_not_reached();
	}

	/*
	 * Can't call entropy_array_ulong_collect() here since we are also called
	 * from entropy_seed(), which is used to seed entropy_minirand().
	 * Hence we manually shuffle the registers.
	 */

	memcpy(r, env, sizeof r);
	SHUFFLE_ARRAY_WITH(rand31_u32, r);

	SHA1_INPUT(ctx, env);				/* "env" is an array */
	SHA1_INPUT(ctx, r);
}

/**
 * Collect entropy from environment.
 */
static void
entropy_collect_environ(SHA1_context *ctx)
{
	extern char **environ;
	size_t i, j;
	const char *str[RANDOM_SHUFFLE_MAX];

	for (i = 0, j = 0; NULL != environ[i]; i++) {
		str[j++] = environ[i];
		if (RANDOM_SHUFFLE_MAX == j) {
			entropy_array_string_collect(ctx, str, RANDOM_SHUFFLE_MAX);
			j = 0;
		}
	}
	if (j != 0)
		entropy_array_string_collect(ctx, str, j);
	sha1_feed_ulong(ctx, i);
}

/**
 * Collect a few pseudo-random numbers.
 */
static void
entropy_collect_minirand(SHA1_context *ctx)
{
	ulong rn[RANDOM_SHUFFLE_MAX];
	int i = 0;

	while (i < RANDOM_SHUFFLE_MAX) {
		rn[i++] = entropy_minirand();
	}

	entropy_array_ulong_collect(ctx, rn, N_ITEMS(rn));
}

/**
 * Collect entropy from current time.
 */
static void
entropy_collect_time(SHA1_context *ctx)
{
	tm_nano_t now;

	tm_precise_time(&now);
	SHA1_INPUT(ctx, now);
}

/**
 * Collect stack garbage.
 */
static void
entropy_collect_garbage(SHA1_context *ctx)
{
	ulong garbage[RANDOM_SHUFFLE_MAX];	/* Left un-initialized on purpose */

#ifndef ALLOW_UNINIT_VALUES
	ZERO(&garbage);
#endif

	entropy_array_ulong_collect(ctx, garbage, N_ITEMS(garbage));
}

/**
 * Collect volume entropy.
 */
static void
entropy_collect_vfs(SHA1_context *ctx)
{
	const char *path[RANDOM_SHUFFLE_MAX];
	size_t i = 0;

	path[i++] = ".";
	path[i++] = "/";

	if (is_running_on_mingw()) {
		path[i++] = "C:/";
		path[i++] = "D:/";
	} else {
		path[i++] = "/tmp";
		path[i++] = "/var/run";
	}

	g_assert(i <= N_ITEMS(path));

	entropy_array_statvfs_collect(ctx, path, i);
}

/**
 * Collect entropy from current thread.
 */
static void
entropy_collect_thread(SHA1_context *ctx)
{
	thread_t th = thread_current();

	SHA1_INPUT(ctx, th);
}

/**
 * Collect entropy from initial IP gateway.
 */
static void
entropy_collect_gateway(SHA1_context *ctx)
{
	static host_addr_t addr;
	static bool done;

	/*
	 * This is for entropy computation, we do not need the accurate current
	 * IP gateway information, hence cache results, forever.
	 *		--RAM, 2022-12-29
	 */

	if G_UNLIKELY(!done) {
		done = TRUE;
		if (-1 == getgateway(&addr))
			sha1_feed_errno(ctx);
	}

	SHA1_INPUT(ctx, addr);
}

/**
 * Collect entropy from host.
 *
 * This uses the name of the host.
 */
static void
entropy_collect_host(SHA1_context *ctx)
{
	const char *name;

	name = local_hostname();
	sha1_feed_string(ctx, name);

	/*
	 * We used to call
	 *
	 * 	name_to_host_addr(name, NET_TYPE_NONE);
	 *
	 * to also lookup the host name and get its IP address but depending
	 * on the DNS configuration, that could cause long delays, even up to
	 * a timeout, causing the auto-initialization phase to also timeout,
	 * leading to a crash!
	 *		--RAM, 2016-02-16
	 */
}

/**
 * Collect entropy from VMM information.
 */
static void
entropy_collect_vmm(SHA1_context *ctx)
{
	void *p, *q;
	void *ptr[3];

	ptr[0] = deconstify_pointer(vmm_trap_page());
	ptr[1] = p = vmm_alloc(1);
	ptr[2] = q = vmm_alloc(1);

	entropy_array_pointer_collect(ctx, ptr, N_ITEMS(ptr));

	vmm_free(p, 1);
	vmm_free(q, 1);
}

typedef void (*digest_collector_cb_t)(sha1_t *digest);

static digest_collector_cb_t entropy_digest_source[] = {
	halloc_stats_digest,
	palloc_stats_digest,
	random_stats_digest,
	thread_stats_digest,
	tmalloc_stats_digest,
	vmm_stats_digest,
	xmalloc_stats_digest,
	zalloc_stats_digest,
};

/**
 * Collect entropy from various statistics digests.
 */
static void
entropy_collect_digests(SHA1_context *ctx)
{
	sha1_t digests[N_ITEMS(entropy_digest_source)];
	size_t i;

	STATIC_ASSERT(N_ITEMS(entropy_digest_source) == N_ITEMS(digests));

	for (i = 0; i < N_ITEMS(entropy_digest_source); i++) {
		(*entropy_digest_source[i])(&digests[i]);
	}

	/*
	 * Since some of the digests above may be predictible at this stage,
	 * randomize each of them by shuffling their bytes.
	 */

	for (i = 0; i < N_ITEMS(digests); i++) {
		shuffle_with(entropy_minirand, VARLEN(digests[i]), 1);
	}

	entropy_array_data_collect(ctx, ENTROPY_SHA1,
		digests, N_ITEMS(digests), sizeof digests[0]);
}

/**
 * Collect entropy from two random statistics digest.
 */
static void
entropy_collect_two_digests(SHA1_context *ctx)
{
	size_t i;

	for (i = 0; i < 2; i++) {
		sha1_t digest;
		size_t n =
			random_upto(entropy_minirand, N_ITEMS(entropy_digest_source) - 1);

		(*entropy_digest_source[n])(&digest);
		shuffle_with(entropy_minirand, VARLEN(digest), 1);
		SHA1_INPUT(ctx, digest);
	}
}

/**
 * Collect entropy based on CPU time used and scheduling delays.
 */
static void
entropy_collect_timing(SHA1_context *ctx, bool slow)
{
	double v[4];
	tm_nano_t before, after;

	tm_precise_time(&before);

	v[0] = tm_cputime(&v[1], &v[2]);

	if (slow) {
		compat_usleep_nocancel(100);	/* 0.1 ms */
	} else {
		entropy_delay();			/* create small, unpredictable delay */
	}

	tm_precise_time(&after);
	v[3] = tm_precise_elapsed_f(&after, &before);

	entropy_array_double_collect(ctx, v, N_ITEMS(v));
}

/**
 * Feed the (shuffled) SHA1 context to itself.
 */
static void
entropy_self_feed(SHA1_context *ctx)
{
	char cbytes[sizeof(SHA1_context)];
	int i;

	/*
	 * Because there are about 108 bytes to shuffle, rand31_u32()
	 * is a poor source of randomness: we don't have enough bits
	 * of context to produce all the combinations.  Therefore, the
	 * shuffling is biased.
	 *
	 * To randomize things a little more, we perform our shuffling
	 * several times.
	 *
	 * Here, we want to introduce as much chaos as possible in the original
	 * seed computation to prevent several computers executing the same
	 * logic (this program) at around the same time with similar machines
	 * from producing the same seed.
	 *
	 * We cannot use entropy_minirand() to shuffle since we can be called
	 * from entropy_seed() when we're precisely seeding the initial context
	 * used by entropy_minirand().
	 */

	memcpy(cbytes, ctx, sizeof cbytes);
	for (i = 0; i < 7; i++) {
		tm_nano_t now;
		tm_precise_time(&now);
		rand31_addrandom(VARLEN(now));
		SHUFFLE_ARRAY_WITH(rand31_u32, cbytes);
	}
	SHA1_INPUT(ctx, cbytes);
}

/**
 * Collect entropy and fill supplied SHA1 buffer with 160 random bits.
 *
 * @param digest			where generated random 160 bits are output
 * @param can_malloc		if FALSE, make sure we never malloc()
 * @param slow				whether we can sleep for 0.1 ms
 *
 * @attention
 * This is a slow operation, and the routine can even sleep for 0.1 ms, so it
 * must be called only when a truly random seed is required, ideally only
 * during initialization.
 */
void G_COLD
entropy_collect_internal(sha1_t *digest, bool can_malloc, bool slow)
{
	static tm_nano_t last;
	SHA1_context ctx;
	tm_nano_t start, end;
	entropy_cb_t fn[RANDOM_SHUFFLE_MAX];
	size_t i = 0;

	/*
	 * Get random entropy from the system.
	 */

	tm_precise_time(&start);

	SHA1_reset(&ctx);
	SHA1_INPUT(&ctx, start);

	if (can_malloc) {
		fn[i++] = entropy_collect_randomness;
		fn[i++] = entropy_collect_user;
		fn[i++] = entropy_collect_login;
		fn[i++] = entropy_collect_pw;
		fn[i++] = entropy_collect_filesystem;
		fn[i++] = entropy_collect_free_space;
		fn[i++] = entropy_collect_ttyname;
		fn[i++] = entropy_collect_vmm;
		fn[i++] = entropy_collect_thread;
		fn[i++] = entropy_collect_gateway;
		fn[i++] = entropy_collect_host;
		fn[i++] = entropy_collect_vfs;
		fn[i++] = entropy_collect_digests;	/* No mem alloc, but SLOW! */

		g_assert(i <= N_ITEMS(fn));
	}

	fn[i++] = entropy_collect_cpu;
	fn[i++] = entropy_collect_environ;
	fn[i++] = entropy_collect_user_id;
	fn[i++] = entropy_collect_process_id;
	fn[i++] = entropy_collect_compile_time;
	fn[i++] = entropy_collect_stdio;
	fn[i++] = entropy_collect_usage;
	fn[i++] = entropy_collect_uname;
	fn[i++] = entropy_collect_pointers;
	fn[i++] = entropy_collect_file_amount;
	fn[i++] = entropy_collect_minirand;
	fn[i++] = entropy_collect_time;
	fn[i++] = entropy_collect_garbage;
	fn[i++] = entropy_self_feed;
	fn[i++] = entropy_collect_two_digests;

	g_assert(i <= N_ITEMS(fn));

	entropy_array_cb_collect(&ctx, fn, i);

	/*
	 * Finish by collecting various information that cannot be easily
	 * dispatched randomly due to parameters or conditions.
	 */

	entropy_collect_timing(&ctx, slow);

	{
		double v[2];

		v[0] = tm_precise_elapsed_f(&start, &last);
		last = start;		/* struct copy */

		tm_precise_time(&end);
		SHA1_INPUT(&ctx, end);
		v[1] = tm_precise_elapsed_f(&end, &start);

		entropy_array_double_collect(&ctx, v, N_ITEMS(v));
	}

	/*
	 * Done, finalize SHA1 computation into supplied digest buffer.
	 */

	SHA1_result(&ctx, digest);

	/*
	 * Merge entropy from all the previous calls to make this as unique
	 * a random bitstream as possible.
	 */

	entropy_merge(digest);
}

/**
 * Randomly feed the SHA1 context to itself 20% of the time.
 */
static void G_COLD
entropy_self_feed_maybe(SHA1_context *ctx)
{
	if (random_upto(rand31_u32, 999) < 200)
		entropy_self_feed(ctx);
}

/**
 * Seed the entropy_minirand() context variable, once.
 *
 * We're collecting changing and contextual data, to be able to compute an
 * initial 160-bit value, which is better than the default zero value.
 *
 * @note
 * As a side effect, this routine seeds a random value into "entropy_previous".
 */
static void G_COLD
entropy_seed(struct entropy_minictx *c)
{
	extern char **environ;
	ulong garbage[16];		/* Left uninitialized on purpose */
	const char *str[RANDOM_SHUFFLE_MAX];
	SHA1_context ctx;
	size_t i, j;
	tm_nano_t now;

	/*
	 * This routine must not allocate any memory because it will be called
	 * very early during initialization.
	 */

#define ENTROPY_CONTEXT_FEED	entropy_self_feed_maybe(&ctx)

#define ENTROPY_SHUFFLE_FEED(a, f) G_STMT_START {				\
	size_t x;													\
	SHUFFLE_ARRAY_WITH(rand31_u32, a);							\
	for (x = 0; x < N_ITEMS(a); x++)						\
		f(&ctx, a[x]);											\
	ENTROPY_CONTEXT_FEED;										\
} G_STMT_END

	SHA1_reset(&ctx);

	tm_precise_time(&now);		/* Do not use tm_now_exact(), it's too soon */
	SHA1_INPUT(&ctx, now);

	j = popcount(now.tv_nsec);
	for (i = 0; i <= j; i++) {
		ENTROPY_CONTEXT_FEED;
	}

	{
		ulong along[] = { now.tv_sec, now.tv_nsec,
			getpid(), getppid(), getuid(), getgid() };
		ENTROPY_SHUFFLE_FEED(along, sha1_feed_ulong);
	}

	entropy_collect_cpu(&ctx);
	ENTROPY_CONTEXT_FEED;

	{
		int afd[3] = { STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO };
		ENTROPY_SHUFFLE_FEED(afd, sha1_feed_fstat);
	}

	/* Feed new randomness to the rand31 PRNG */

	{
		sha1_t hash;
		uint8 data[SHA1_RAW_SIZE];

		SHA1_intermediate(&ctx, &hash);
		memcpy(data, &hash, sizeof data);
		SHUFFLE_ARRAY_WITH(rand31_u32, data);
		SHA1_INPUT(&ctx, data);
		tm_precise_time(&now);
		SHA1_INPUT(&ctx, now);
		SHA1_intermediate(&ctx, &hash);
		rand31_addrandom(VARLEN(hash));
	}

	for (i = 0, j = 0; NULL != environ[i]; i++) {
		str[j++] = environ[i];
		if (RANDOM_SHUFFLE_MAX == j) {
			ENTROPY_SHUFFLE_FEED(str, sha1_feed_string);
			j = 0;
		}
	}
	sha1_feed_ulong(&ctx, i);
	if (j != 0) {
		shuffle_with(rand31_u32, str, j, sizeof str[0]);
		for (i = 0; i < j; i++) {
			sha1_feed_string(&ctx, str[i]);
		}
	}
	ENTROPY_CONTEXT_FEED;

	{
		void *aptr[] = { environ, &now, sbrk(0), entropy_seed };
		ENTROPY_SHUFFLE_FEED(aptr, sha1_feed_pointer);
	}

#ifdef MINGW32
#define ENTROPY_TMP	"C:/WINDOWS/Temp"
#define ENTROPY_VAR	"D:/"
#else
#define ENTROPY_TMP	"/tmp"
#define ENTROPY_VAR	"/var"
#endif

	{
		const char *astr[] =
			{ ".", "..", "../..", "../../..", "/", ENTROPY_TMP, ENTROPY_VAR };
		ENTROPY_SHUFFLE_FEED(astr, sha1_feed_stat);
	}

#ifndef ALLOW_UNINIT_VALUES
	ZERO(&garbage);
#endif

	SHUFFLE_ARRAY_WITH(rand31_u32, garbage);
	SHA1_INPUT(&ctx, garbage);
	ENTROPY_CONTEXT_FEED;

	entropy_delay();
	tm_precise_time(&now);
	SHA1_INPUT(&ctx, now);

	tm_precise_time(&now);
	j = popcount(now.tv_nsec * 11);
	for (i = 0; i <= j; i++) {
		ENTROPY_CONTEXT_FEED;
	}

	/* Thread runtime statistics, including QID lookups, locks, etc... */

	{
		sha1_t d;

		thread_stats_digest(&d);
		shuffle_with(rand31_u32, VARLEN(d), 1);
		SHA1_INPUT(&ctx, d);
		ENTROPY_CONTEXT_FEED;
	}

	/* Partial SHA1 result */

	{
		sha1_t hash;
		const void *p = &hash;
		uint32 v, n;

		SHA1_intermediate(&ctx, &hash);
		p = peek_be32_advance(p, &v);

		entropy_delay();
		tm_precise_time(&now);
		n = popcount(peek_be32(p) + now.tv_nsec);
		j = UINT32_ROTR(v, n) & 0xff;
		for (i = 0; i <= j; i++) {
			ENTROPY_CONTEXT_FEED;
		}

		SHA1_intermediate(&ctx, &hash);
		sha1_feed_uint(&ctx, peek_be32(&hash));
	}

	entropy_delay();
	tm_precise_time(&now);

	{
		double r = random_double_generate(rand31_u32);
		double usr, sys, cpu = tm_cputime(&usr, &sys);
		double adouble[6] = { cpu, usr, sys, r,
			now.tv_nsec / 101.0, now.tv_nsec / (now.tv_sec + 0.1) };
		ENTROPY_SHUFFLE_FEED(adouble, sha1_feed_double);
	}

#undef ENTROPY_SHUFFLE_FEED
#undef ENTROPY_CONTEXT_FEED

	tm_precise_time(&now);
	SHA1_INPUT(&ctx, now);

	/*
	 * Before initializing the KISS PRNG, initialize a random number
	 * as previous entropy.
	 *
	 * This variable is used to perturb the entropy we are generating by
	 * summing all the entropy we generated so far.  Better seed it with a
	 * random value that will remain unknown and unpredictable.
	 *
	 * We know that this routine will be called once, the first time we need
	 * a random number during entropy computations, when we call
	 * entropy_minirand().  At that time, entropy_previous must be zero.
	 */

	{
		bigint_t v;
		bigint_use(&v, VARLEN(entropy_previous));
		g_assert(bigint_is_zero(&v));
	}

	SHA1_intermediate(&ctx, &entropy_previous);		/* No need for lock */

	entropy_delay();
	tm_precise_time(&now);
	SHA1_INPUT(&ctx, now);		/* Hide value in "entropy_previous" */

	/*
	 * Now seed the KISS PRNG.
	 */

	{
		sha1_t hash;
		const void *p = &hash;
		uint32 v;

		SHA1_result(&ctx, &hash);
		p = peek_be32_advance(p, &c->x);
		p = peek_be32_advance(p, &c->y);
		p = peek_be32_advance(p, &c->z);
		p = peek_be32_advance(p, &v);
		c->c = (v ^ peek_be32(p)) % ENTROPY_KISS_MULT;

		/*
		 * The `y' context variable must not be seeded with zero, or it
		 * will only produce zeroes.  Find another random value.
		 */

		for (v = 0; v < 100 && 0 == c->y; v++) {
			c->y = rand31_u32();
		}

		if (0 == c->y)
			s_error("%s(): no luck with rand31_u32()", G_STRFUNC);
	}
}

/**
 * Fold extra entropy bytes in place, putting result in the trailing n bytes.
 *
 * @return pointer to the start of the folded trailing n bytes in the digest.
 */
static void *
entropy_fold(sha1_t *digest, size_t n)
{
	sha1_t result;
	bigint_t h, v;

	g_assert(size_is_non_negative(n));
	g_assert(n < SHA1_RAW_SIZE);

	bigint_use(&v, VARLEN(result));
	bigint_use(&h, PTRLEN(digest));

	bigint_zero(&v);

	while (!bigint_is_zero(&h)) {
		bigint_add(&v, &h);
		bigint_rshift_bytes(&h, n);
	}

	bigint_copy(&h, &v);

	return &digest->data[SHA1_RAW_SIZE - n];
}

/**
 * Functions to call when entropy information is needed.
 */
struct entropy_ops {
	void (*ent_collect)(sha1_t *digest);
	void (*ent_mini_collect)(sha1_t *digest);
	uint32 (*ent_random)(void);
	uint32 (*ent_random_safe)(void);
	void (*ent_fill)(void *buffer, size_t len);
};

static struct entropy_ops entropy_ops;

/**
 * Internal wrapper to collect 160 bits of entropy via AJE.
 */
static void
entropy_aje_collect(sha1_t *digest)
{
	aje_random_bytes(PTRLEN(digest));
}

/**
 * When AJE (Alea Jacta Est) has been initialized, we can use it as our main
 * entropy source.  Hence redirect all entropy requests to that layer.
 */
void G_COLD
entropy_aje_inited(void)
{
	entropy_ops.ent_collect      = entropy_aje_collect;
	entropy_ops.ent_mini_collect = entropy_aje_collect;
	entropy_ops.ent_random       = aje_rand_strong;
	entropy_ops.ent_random_safe  = aje_rand_strong;
	entropy_ops.ent_fill         = aje_random_bytes;
	atomic_mb();
}

/**
 * Collect entropy and fill supplied SHA1 buffer with 160 random bits.
 *
 * It should be called only when a truly random seed is required, ideally only
 * during initialization.
 *
 * @attention
 * This is a slow operation, and the routine will even sleep for 0.1 ms the
 * first time it is invoked.
 */
static void G_COLD
entropy_do_collect(sha1_t *digest)
{
	static bool done;

	misc_init();		/* Required since we have to call parse_uint32() */

	entropy_collect_internal(digest, TRUE, !done);
	done = TRUE;
}

/**
 * Collect minimal entropy, making sure no memory is allocated, and fill
 * supplied SHA1 buffer with 160 random bits.
 *
 * @attention
 * This is a slow operation, so it must be called only when a truly random
 * seed is required.
 */
static void G_COLD
entropy_do_minimal_collect(sha1_t *digest)
{
	entropy_collect_internal(digest, FALSE, FALSE);
}

/**
 * Random number generation based on entropy collection (without any memory
 * allocation).
 *
 * This is a strong random number generator, but it is very slow and should
 * be reserved to low-level initializations, before the ARC4 random number
 * has been properly seeded.
 *
 * @return 32-bit random number.
 */
static uint32
entropy_do_random(void)
{
	static sha1_t digest;
	static void *p = &digest;
	static spinlock_t entropy_random_slk = SPINLOCK_INIT;
	uint32 rnd;

	/*
	 * Collect entropy again once we have exhausted reading from the pool.
	 */

	spinlock_hidden(&entropy_random_slk);

	if G_UNLIKELY(&digest == p) {
		sha1_t tmp;

		spinunlock_hidden(&entropy_random_slk);

		entropy_minimal_collect(&tmp);

		spinlock_hidden(&entropy_random_slk);

		digest = tmp;			/* struct copy */
		p = ptr_add_offset(VARLEN(digest));
	}

	/*
	 * Get the next 32-bit value from the pool, moving right to left.
	 */

	p = ptr_add_offset(p, -4);
	rnd = peek_be32(p);

	spinunlock_hidden(&entropy_random_slk);

	return rnd;
}

/**
 * Early fallback for entropy_random_safe() until AJE is up.
 *
 * @return 32-bit random number.
 */
static uint32
entropy_do_random_safe(void)
{
	return rand31_u32();
}

/**
 * Fill supplied buffer with random entropy bytes.
 *
 * Memory allocation may happen during this call.
 *
 * @param buffer	buffer to fill
 * @param len		buffer length, in bytes
 */
static void
entropy_do_fill(void *buffer, size_t len)
{
	size_t complete, partial, i;
	void *p = buffer;

	g_assert(buffer != NULL);
	g_assert(size_is_non_negative(len));

	complete = len / SHA1_RAW_SIZE;
	partial = len - complete * SHA1_RAW_SIZE;

	for (i = 0; i < complete; i++) {
		sha1_t digest;

		entropy_collect(&digest);
		p = mempcpy(p, &digest, SHA1_RAW_SIZE);
	}

	if (partial != 0) {
		sha1_t digest;
		void *folded;

		entropy_collect(&digest);
		folded = entropy_fold(&digest, partial);
		p = mempcpy(p, folded, partial);
	}

	g_assert(ptr_diff(p, buffer) == len);
}

/**
 * Collect entropy and fill supplied SHA1 buffer with 160 random bits.
 *
 * It should be called only when a truly random seed is required, ideally only
 * during initialization.
 *
 * @attention
 * This is a slow operation, and the routine will even sleep for 0.1 ms the
 * first time it is invoked.
 *
 * @note
 * Once AJE has been initialized, this is transparently re-routed there and
 * the call becomes more efficient by several orders of magnitude!
 */
void G_COLD
entropy_collect(sha1_t *digest)
{
	return entropy_ops.ent_collect(digest);
}

/**
 * Collect minimal entropy, making sure no memory is allocated, and fill
 * supplied SHA1 buffer with 160 random bits.
 *
 * @attention
 * This is a slow operation, so it must be called only when a truly random
 * seed is required.
 *
 * @note
 * Once AJE has been initialized, this is transparently re-routed there and
 * the call becomes more efficient by several orders of magnitude!
 */
void G_COLD
entropy_minimal_collect(sha1_t *digest)
{
	return entropy_ops.ent_mini_collect(digest);
}

/**
 * Random number generation based on entropy collection (without any memory
 * allocation).
 *
 * This is a strong random number generator, but it is very slow and should
 * be reserved to low-level initializations, before the AJE random number
 * has been properly seeded.
 *
 * @note
 * Once AJE has been initialized, this is transparently re-routed there and
 * the call becomes more efficient by several orders of magnitude!
 *
 * @return 32-bit random number.
 */
uint32
entropy_random(void)
{
	return entropy_ops.ent_random();
}

/**
 * Random number generation based on entropy collection (without any memory
 * allocation).
 *
 * When AJE has been initialized, this is a strong random number generator.
 * Otherwise it is a weak one.
 *
 * The above means this call is intended to avoid deadly recursions during
 * early initializations.  This is why this is an internal call and is
 * not exported.
 *
 * @return 32-bit random number.
 */
static uint32
entropy_random_safe(void)
{
	return entropy_ops.ent_random_safe();
}

/**
 * Fill supplied buffer with random entropy bytes.
 *
 * Memory allocation may happen during this call.
 *
 * @note
 * Once AJE has been initialized, this is transparently re-routed there.
 *
 * @param buffer	buffer to fill
 * @param len		buffer length, in bytes
 */
void
entropy_fill(void *buffer, size_t len)
{
	return entropy_ops.ent_fill(buffer, len);
}

static struct entropy_ops entropy_ops = {
	entropy_do_collect,			/* ent_collect */
	entropy_do_minimal_collect,	/* ent_mini_collect */
	entropy_do_random,			/* ent_random */
	entropy_do_random_safe,		/* ent_random_safe */
	entropy_do_fill,			/* ent_fill */
};

/**
 * Get the entropy nonce, a number used to alter time-based entropy collection
 * in a way that cannot be guessed by an outsider.
 *
 * @return the nonce to use for the session / computation.
 */
uint32
entropy_nonce(void)
{
	static uint32 base;
	static int nused;
	int c;

	c = atomic_int_inc(&nused);

	/*
	 * Should two threads collide and reach the maximum base usage time,
	 * we want them to both recompute the base.  Sure, we could make the
	 * first thread do that only by using an equality test, but this adds
	 * more entropy because the computation becomes less deterministic due
	 * to the possible lock contentions.
	 */

	if G_UNLIKELY(0 == base || ENTROPY_NONCE_MAX <= c) {
		static spinlock_t base_slk = SPINLOCK_INIT;
		uint32 newbase;

		do {
			newbase = entropy_random_safe();
		} while (0 == newbase);

		spinlock_hidden(&base_slk);
		base = newbase;
		nused = 0;
		spinunlock_hidden(&base_slk);
	}

	return base + c;		/* Number used once */
}

/**
 * Compute hash of current time, using the most precise clock time we have.
 */
static uint32
entropy_clock_time(void)
{
	tm_nano_t now;

	tm_precise_time(&now);

	return integer_hash_fast(now.tv_nsec) +
		integer_hash_fast(now.tv_sec + entropy_nonce());
}

/**
 * Harvest entropy from the current time.
 */
void
entropy_harvest_time(void)
{
	uint16 rnd = entropy_clock_time();	/* Yes, truncated to 16 bits */

	random_pool_append(VARLEN(rnd));
}

/**
 * Harvest entropy from a single value.
 *
 * @param p		pointer to value from which we want to harvest entropy
 * @param len	length of the data to read
 */
void
entropy_harvest_single(const void *p, size_t len)
{
	g_assert(p != NULL);
	g_assert(size_is_positive(len));

	if (len <= sizeof(long)) {
		entropy_harvest_small(p, len, NULL);
	} else {
		entropy_harvest_many(p, len, NULL);
	}
}

/**
 * Harvest entropy from a NULL-terminated list of (pointer, length).
 *
 * The overall entropy is supposed to be poor and is therefore limited
 * to the collection of 4 bytes, using a CRC32 computation.
 *
 * @param p		pointer to first value
 * @param len	length of first value
 * @param ...	NULL-terminated list of (p, len)
 */
void
entropy_harvest_small(const void *p, size_t len, ...)
{
	uint32 c, h;
	va_list ap;

	g_assert(p != NULL);
	g_assert(size_is_positive(len));

	h = entropy_clock_time();
	c = crc32_update(-1U, VARLEN(h));
	c = crc32_update(c, p, len);

	va_start(ap, len);

	for (
		p = va_arg(ap, const void *);
		p != NULL;
		p = va_arg(ap, const void *)
	) {
		len = va_arg(ap, size_t);
		g_assert(size_is_positive(len));
		c = crc32_update(c, p, len);
	}

	va_end(ap);

	random_pool_append(VARLEN(c));
}

/**
 * Harvest entropy from a NULL-terminated list of (pointer, length).
 *
 * The overall entropy is supposed to be large and is therefore harvested
 * through a succession of CRC32 computations.
 *
 * @param p		pointer to first value
 * @param len	length of first value
 * @param ...	NULL-terminated list of (p, len)
 */
void
entropy_harvest_many(const void *p, size_t len, ...)
{
	uint32 nonce = entropy_nonce();
	tm_nano_t now;
	va_list ap;
	uint32 c[8];		/* Arbitrary amount of values, greater than 2  */
	size_t i = 0;

	g_assert(p != NULL);
	g_assert(size_is_positive(len));

	tm_precise_time(&now);

	c[i++] = crc32_update(-1U, VARLEN(now));
	c[i++] = crc32_update(nonce, p, len);

	va_start(ap, len);

	for (
		p = va_arg(ap, const void *);
		p != NULL;
		p = va_arg(ap, const void *)
	) {
		len = va_arg(ap, size_t);
		if (N_ITEMS(c) == i) {
			random_pool_append(ARYLEN(c));
			i = 0;
		}
		c[i++] = crc32_update(nonce, p, len);
	}

	va_end(ap);

	random_pool_append(c, ptr_diff(&c[i], &c[0]));
}

/* vi: set ts=4 sw=4 cindent: */
