/*
 * random.c -- A (strong?) random number generator for SunSolaris
 *
 * Version 0.7a, last modified 17-Jan-2002
 *
 * Copyright (c) Andreas Maier, 2000, 2001, 2002. All rights reserved.
 * Andreas Maier <andi@cosy.sbg.ac.at>
 *
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, and the entire permission notice in its entirety,
 *    including the disclaimer of warranties.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote
 *    products derived from this software without specific prior
 *    written permission.
 *
 * ALTERNATIVELY, this product may be distributed under the terms of
 * the GNU Public License, in which case the provisions of the GPL are
 * required INSTEAD OF the above restrictions.  (This clause is
 * necessary due to a potential bad interaction between the GPL and
 * the restrictions contained in a BSD-style copyright.)
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * These routines provide /dev/random and /dev/urandom, similar to the
 * devices found in Linux, and used by cryptographic software (e.g. open-ssl).
 */

/*
 * Acknowledgements:
 * =================
 *
 * This software is a poor mans adaptation of 'random.c', which is part
 * of the Linux kernel source, and written by Theodore Ts'o.
 * For a 'theory of operation' look at Theodore Ts'o code. You can
 * find a very good and extensive description there.
 *
 * Details on Sun Solaris device drivers can be found in
 * 'Writing Device Drivers', Sun Microsystems, PartNo: 805-3024-10.
 * This document is available online at the Sun Developer Connection.
 *
 * Thanks to many people for suggestions, bug fixes and tests. Thanks to
 * Willi Burmeister for the preparation of a Sun packages. Thanks to
 * Hans Werner Strube for the 64bit port.
 *
 * Any flaws in the design are solely my responsibility, and should
 * not be attributed to Theodore Ts'o or Sun Microsystems.
 */

/*
 * Cryptographic software needs good random numbers. On way to provide
 * such numbers is by gathering various unpredictable events in the
 * kernel. This collected information can than be read on a character
 * interface. Unfortunatly Sun Solaris (as of version 7) does not have
 * such an interface. This is a try to fix the problem by means of a
 * loadable kernel module.
 *
 *
 * NOTE NOTE NOTE NOTE  NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE 
 *
 * -) The sources for randomness are poor.
 * -) There is no such thing as a randomness estimate.
 * -) /dev/random and /dev/urandom are the same (no blocking).
 * -) No ioctls
 *
 * NOTE NOTE NOTE NOTE  NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE 
 */

/*
 * Installation:
 * =============
 *
 * A Sun package with precompiled drivers for 32bit and 64bit can be
 * found at 'http://www.cosy.sbg.ac.at/~andi/'. If you want to compile
 * the driver yourself use the following procedure:
 *
 * Create a file '/usr/kernel/drv/random.conf' with contents similar to:
 *	name="random" parent="pseudo" instance=0;
 *
 * Append the following lines to '/etc/devlink.tab':
 *	type=ddi_pseudo;name=random;addr=0;minor=random	\M0
 *	type=ddi_pseudo;name=random;addr=0;minor=urandom	\M0
 *
 *	(note that _one_ TAB _must_ separate the second argument)
 *	(note that add_drv may complain on the last line; I do not know
 *	 why; it may depend on the patches you installed; you can
 *	 ignore the complain or remove this last line)
 *
 * Compile this file (gcc 2.8.1 works for me):
 *	% gcc -D_KERNEL -O2 -c random.c
 *	% ld -r -o random random.o
 *
 *	(note that this would produce a 32bit driver which is good for
 *	 Solaris 2.6 and earlyer and all Solaris versions in 32bit mode)
 *	 with Sun CC use:
 *		(32bit) cc -D_KERNEL -O -c random.c
 *		(64bit) cc -D_KERNEL -O -xarch=v9 -c random.c
 *
 * Install the driver:
 *	# cp random /usr/kernel/drv
 *	# add_drv -m '* 0644 root sys' random
 *
 * Preload random data at startup:
 *	The random pool can be initialized at system startup by
 *	a script containing a line simmilar to:
 *		dd if=$random_seed_file of=/dev/urandom
 */

/*
 * History:
 * ========
 *
 * v0.7a:
 *	patch to compile on Solaris 2.4: #ifdef CB_REV
 * v0.7:
 *	prevent denial-of-service attack by returning no more than
 *		(MAX_BEFORE_YIELD * HASH_BUFFER_SIZE / 2) bytes
 * v0.6:
 *	cast bcopy and bzero to work with Solaris 2.5.1
 *	comments on the SHA f()-functions
 * v0.5:
 *	add_entropy_words: added some MASK calls to prevent index
 *		overflow if i is odd
 *	SHATransform: more documentation to the SHA f()-functions,
 *		different implementation of f3() function
 *	extract_entropy: fold middle word by half but hide unfolded
 *		part (independent of cpu byte order)
 *	add_timer_rand/add_pid_rand: fold 64 bit timer value into 32 bit
 *		variable to get more randomness.
 * v0.4:
 *	line 676: wrong size of data in bcopy (source of the
 *		repetition error in random data)
 * v0.3:
 *	second parameter of rand_detach is of type ddi_detach_cmd_t
 *	ticks is type time_t
 *	pid is type pid_t
 * v0.2:
 *	rand_chpoll (for poll/select system call) added
 * v0.1:
 *	Initial Release.
 */

#include <sys/types.h>
#include <sys/param.h>
#include <sys/errno.h>
#include <sys/uio.h>
#include <sys/buf.h>
#include <sys/modctl.h>
#include <sys/open.h>
#include <sys/kmem.h>
#include <sys/poll.h>
#include <sys/conf.h>
#include <sys/cmn_err.h>
#include <sys/stat.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/file.h>



#define RAND_DEV "random"	/* more or less standard device names */
#define URAND_DEV "urandom"

#define POOLWORDS 128	/* Power of 2 - note that this is 32-bit words */

#define MAX_BEFORE_YIELD 3	/* Max uiomove()s to do */


struct rand_state {
	dev_info_t * dip;		/* device (kernel needs this) */
	struct pollhead pollhead;	/* for chpoll */
	kmutex_t guard;			/* data update mutex */
	unsigned int add_ptr;
	unsigned int entropy_count;
	uint32_t pool[POOLWORDS];	/* pool of random numbers */
};


static int rand_getinfo (dev_info_t * dip, ddi_info_cmd_t infocmd,
			 void * arg, void ** result);
static int rand_attach (dev_info_t * dip, ddi_attach_cmd_t cmd);
static int rand_detach (dev_info_t * dip, ddi_detach_cmd_t cmd);
static int rand_open (dev_t * devp, int flag, int otyp, cred_t * credp);
static int rand_close (dev_t devp, int flag, int otyp, cred_t * credp);
/*
static int rand_ioctl (dev_t dev, int cmd, int arg, int mode,
		       cred_t * credp, int * rvalp);
*/
static int rand_read (dev_t dev, uio_t * uiop, cred_t * credp);
static int rand_write (dev_t dev, uio_t * uiop, cred_t * credp);
static int rand_chpoll (dev_t dev, short int events, int anyyet,
			short int * reventsp, struct pollhead ** phpp);
static void add_entropy_words (struct rand_state * r, uint32_t x, uint32_t y);
static int extract_entropy (struct rand_state * r, uio_t * uiop);
static void add_timer_rand (struct rand_state * r);
static void add_pid_rand (struct rand_state * r);


/* device access routines */
static struct cb_ops cb_ops = {
	rand_open,		/* open */
	rand_close,		/* close */
	nodev,			/* strategy (blockdev) */
	nodev,			/* print (blockdev) */
	nodev,			/* dump (blockdev) */
	rand_read,		/* read */
	rand_write,		/* write */
	nodev,			/* ioctl */
	nodev,			/* devmap */
	nodev,			/* mmap */
	nodev,			/* segmap */
	rand_chpoll,		/* chpoll */
	ddi_prop_op,		/* prop_op */
	NULL,			/* STREAMS ops */
	(D_NEW | D_MP),		/* flags */
#ifdef CB_REV
	CB_REV,			/* revision */
	nodev,			/* aread */
	nodev			/* awrite */
#endif
};

/* device setup */
static struct dev_ops dev_ops = {
	DEVO_REV,		/* revision */
	0,			/* refcnt */
	rand_getinfo,		/* getinfo */
	nulldev,		/* identify (obsolete) */
	nulldev,		/* probe */
	rand_attach,		/* attach */
	rand_detach,		/* detach */
	nodev,			/* reset (not impl) */
	&cb_ops,		/* cb_ops */
	NULL,			/* bus_ops */
	NULL			/* power */
};

/* loadable module support */
static struct modldrv modldrv = {
	&mod_driverops,
	"[u]random devices v0.7b",
	&dev_ops
};

static struct modlinkage modlinkage = { MODREV_1, { &modldrv, NULL } };

/* head of per instance data struct */
static void * rs;


/* load the module, alloc mem, and install the module */
int
_init (void)
{
	int err;

	err = ddi_soft_state_init (&rs, sizeof (struct rand_state), 1);
	if (err == 0) {
		err = mod_install (&modlinkage);
		if (err != 0) {
			ddi_soft_state_fini (&rs);
		}
	}

	return (err);
}


/* remove the module, and free mem */
int
_fini (void)
{
	int err;

	err = mod_remove (&modlinkage);
	if (err == 0) {
		ddi_soft_state_fini (&rs);
	}

	return (err);
}


/* supply info needed by the module loader */
int
_info (struct modinfo * modinfop)
{
	return (mod_info (&modlinkage, modinfop));
}


/* create one driver instance */
static int
rand_attach (dev_info_t * dip, ddi_attach_cmd_t cmd)
{
	minor_t instance;
	struct rand_state * rsp;

	switch (cmd) {
	case DDI_ATTACH:
		instance = ddi_get_instance (dip);

		if (ddi_soft_state_zalloc (rs, instance) != DDI_SUCCESS) {
			cmn_err(CE_CONT, "%s%d: can't allocate state\n",
				ddi_get_name(dip), instance);
			return (DDI_FAILURE);
		} else {
			rsp = ddi_get_soft_state (rs, instance);
		}

		if (ddi_create_minor_node (dip, RAND_DEV, S_IFCHR,
			(instance * 2), DDI_PSEUDO, 0) != DDI_SUCCESS) {
				ddi_soft_state_free (rs, instance);
				return (DDI_FAILURE);
		} else if (ddi_create_minor_node (dip, URAND_DEV, S_IFCHR,
			((instance * 2) + 1), DDI_PSEUDO, 0) != DDI_SUCCESS) {
				ddi_remove_minor_node (dip, NULL);
				ddi_soft_state_free (rs, instance);
				return (DDI_FAILURE);
		}

		rsp->dip = dip;
		mutex_init (&(rsp->guard), NULL, MUTEX_DRIVER, NULL);

		/* report our presence */
		ddi_report_dev (dip);

		add_timer_rand (rsp);

		return (DDI_SUCCESS);
		break;
	default:
		return (DDI_FAILURE);
		break;
	}

	/* the sun CC does not like the next line */
	/* return (DDI_FAILURE); */
}


/* destroy one driver instance */
static int
rand_detach (dev_info_t * dip, ddi_detach_cmd_t cmd)
{
	minor_t instance;
	struct rand_state * rsp;

	switch (cmd) {
	case DDI_DETACH:
		ddi_prop_remove_all (dip);
		instance = ddi_get_instance (dip);

		rsp = ddi_get_soft_state (rs, instance);
		if (rsp != NULL) {
			mutex_destroy (&(rsp->guard));
		}

		ddi_remove_minor_node (dip, NULL);
		ddi_soft_state_free (rs, instance);

		return (DDI_SUCCESS);
		break;
	default:
		return (DDI_FAILURE);
		break;
	}

	/* the sun CC does not like the next line */
	/* return (DDI_FAILURE); */
}


/* supply instance specific information */
static int
rand_getinfo (dev_info_t * dip, ddi_info_cmd_t infocmd, void * arg,
	      void ** result)
{
	struct rand_state * rsp;
	int instance;

	instance = getminor ((dev_t) arg) / 2;

	switch (infocmd) {
	case DDI_INFO_DEVT2INSTANCE:
		* result = (void *) instance;
		return (DDI_SUCCESS);
		break;
	case DDI_INFO_DEVT2DEVINFO:
		rsp = ddi_get_soft_state (rs, instance);
		if (rsp != NULL) {
			* result = rsp->dip;

			add_timer_rand (rsp);

			return (DDI_SUCCESS);
		} else {
			return (DDI_FAILURE);
		}
		break;
	default:
		return (DDI_FAILURE);
		break;
	}

	/* the sun CC does not like the next line */
	/* return (DDI_FAILURE); */
}


static int
rand_open (dev_t * devp, int flag, int otyp, cred_t * credp)
{
	minor_t instance;
	struct rand_state * rsp;

	instance = getminor (* devp) / 2;

	/* a character device? */
	if (otyp != OTYP_CHR) {
		return (EINVAL);
	}

	/* valid minor number? */
	rsp = ddi_get_soft_state (rs, instance);
	if (rsp == NULL) {
		return (ENXIO);
	}

	add_pid_rand (rsp);

	return (0);
}


static int
rand_close (dev_t devp, int flag, int otyp, cred_t * credp)
{
	minor_t instance;
	struct rand_state * rsp;

	instance = getminor (devp) / 2;

	/* valid minor number? */
	rsp = ddi_get_soft_state (rs, instance);
	if (rsp == NULL) {
		return (ENXIO);
	}

	add_pid_rand (rsp);

	return (0);
}


static int
rand_read (dev_t dev, uio_t * uiop, cred_t * credp)
{
	struct rand_state * rsp;
	int instance;

	instance = getminor (dev) / 2;

	/* valid minor number? */
	rsp = ddi_get_soft_state (rs, instance);
	if (rsp == NULL) {
		return (ENXIO);
	}

	add_pid_rand (rsp);

	return (extract_entropy (rsp, uiop));
}


static int
rand_write (dev_t dev, struct uio * uiop, cred_t * credp)
{
	struct rand_state * rsp;
	int instance;
	uint32_t buf[16];
	int i;
	int err;

	instance = getminor (dev) / 2;

	rsp = ddi_get_soft_state (rs, instance);
	if (rsp == NULL) {
		cmn_err (CE_CONT, "=== write(): invalied minor\n");
		return (ENXIO);
	}

	/* Only use one buffer full of data per system call (prevent
	 * denial-of-service attacks), so no 'while (uiop->uio_resid > 0)'
	 * loop.
	 */
	err = uiomove ((caddr_t) buf, sizeof (buf), UIO_WRITE, uiop);
	if (err != 0) {
		return (err);
	}

	i = sizeof (buf) / (2 * sizeof (buf[0]));
	while (i > 0) {
		i --;
		add_entropy_words (rsp, buf[2 * i], buf[2 * i + 1]);
	}

	add_pid_rand (rsp);

	return (0);
}


static int
rand_chpoll (dev_t dev, short int events, int anyyet, short int * reventsp,
	     struct pollhead ** phpp)
{
	struct rand_state * rsp;
	int instance;

	instance = getminor (dev) / 2;

	/* valid minor number? */
	rsp = ddi_get_soft_state (rs, instance);
	if (rsp == NULL) {
		return (ENXIO);
	}

	if ((events & (POLLIN | POLLOUT | POLLNORM)) != 0) {
		* reventsp = events & (POLLIN | POLLOUT | POLLNORM);
	} else {
		* reventsp = 0;
		if (anyyet == 0) {
			* phpp = & (rsp->pollhead);
		}
	}
	
	add_pid_rand (rsp);

	return (0);
}


/* add entropy be means of high resolution timer and kernel timer ticks */
static void
add_timer_rand (struct rand_state * r)
{
	time_t ticks;
	hrtime_t nticks;

	nticks = gethrtime();		/* fold 64 bit type into 32 bit type */
	if (drv_getparm (TIME, (unsigned long *)&ticks) == 0) {
		add_entropy_words (r, (uint32_t) (nticks ^ (nticks >> 32)),
				   (uint32_t) ticks);
	}
}


/* add entropy be means of high resolution timer and caller PID */
static void
add_pid_rand (struct rand_state * r)
{
	pid_t pid;
	hrtime_t nticks;

	nticks = gethrtime();		/* fold 64 bit type into 32 bit type */
	if (drv_getparm (PPID, (unsigned long *)&pid) == 0) {
		add_entropy_words (r, (uint32_t) (nticks ^ (nticks >> 32)),
				   (uint32_t) pid);
	}
}


/*
 * This function adds a byte into the entropy "pool".  It does not
 * update the entropy estimate.  The caller must do this if appropriate.
 *
 * This function is tuned for speed above most other considerations.
 *
 * The pool is stirred with a primitive polynomial of the appropriate degree,
 * and then twisted.  We twist by three bits at a time because it's
 * cheap to do so and helps slightly in the expected case where the
 * entropy is concentrated in the low-order bits.
 */

#define MASK(x) ((x) & (POOLWORDS - 1))		/* Convenient abreviation */

#define TAP1 103
#define TAP2 76
#define TAP3 51
#define TAP4 25
#define TAP5 1

static void
add_entropy_words (struct rand_state * r, uint32_t x, uint32_t y)
{
	static uint32_t const twist_table[8] = {
		         0, 0x3b6e20c8, 0x76dc4190, 0x4db26158,
		0xedb88320, 0xd6d6a3e8, 0x9b64c2b0, 0xa00ae278 };
	unsigned int i;

	mutex_enter (&(r->guard));

	i = MASK (r->add_ptr - 2);	/* no need to initialize add_ptr */
	r->add_ptr = i;

	/*
	 * XOR in the various taps.  Even though logically, we compute
	 * x and then compute y, we read in y then x order because most
	 * caches work slightly better with increasing read addresses.
	 */

	y ^= r->pool[MASK(i+TAP1)];	x ^= r->pool[MASK(i+TAP1+1)];
	y ^= r->pool[MASK(i+TAP2)];	x ^= r->pool[MASK(i+TAP2+1)];
	y ^= r->pool[MASK(i+TAP3)];	x ^= r->pool[MASK(i+TAP3+1)];
	y ^= r->pool[MASK(i+TAP4)];	x ^= r->pool[MASK(i+TAP4+1)];
	y ^= r->pool[MASK(i+TAP5)];	x ^= r->pool[MASK(i+TAP5+1)];
	y ^= r->pool[i];		x ^= r->pool[MASK(i+1)];
	r->pool[i] = (y >> 3) ^ twist_table[y & 7];
	r->pool[MASK(i+1)] = (x >> 3) ^ twist_table[x & 7];

	mutex_exit (&(r->guard));
}


/*
 * This chunk of code defines a function
 * void SHATransform(__u32 digest[HASH_BUFFER_SIZE + HASH_EXTRA_SIZE],
 * 		     __u32 const data[16])
 * 
 * The function hashes the input data to produce a digest in the first
 * HASH_BUFFER_SIZE words of the digest[] array, and uses HASH_EXTRA_SIZE
 * more words for internal purposes.  (This buffer is exported so the
 * caller can wipe it once rather than this code doing it each call,
 * and tacking it onto the end of the digest[] array is the quick and
 * dirty way of doing it.)
 *
 * It so happens that MD5 and SHA share most of the initial vector
 * used to initialize the digest[] array before the first call:
 * 1) 0x67452301
 * 2) 0xefcdab89
 * 3) 0x98badcfe
 * 4) 0x10325476
 * 5) 0xc3d2e1f0 (SHA only)
 *
 * For /dev/random purposes, the length of the data being hashed is
 * fixed in length (at POOLWORDS words), so appending a bit count in
 * the usual way is not cryptographically necessary.
 */

/*
 * SHA transform algorithm, taken from code written by Peter Gutmann,
 * and placed in the public domain.
 */

/* The SHA f()-functions.
 * The Linux source uses:
 *	#define f1(x,y,z) (z ^ (x & (y ^ z)))
 *	#define f3(x,y,z) ((x & y) + (z & (x ^ y)))
 * The OpenSSL implementation uses:
 *	#define f1(x,y,z) (z ^ (x & (y ^ z)))
 *	#define f3(x,y,z) ((x & y) | (z & (x | y)))
 * The standard defines the functions below. All are equivalent
 * and the compiler optimization removes any perfomance penalties.
 *
 * Robert Stampfli:
 *	                                Number of  sparc instructions:
 *	                                Solaris CC 4.2  GNU GCC 2.95.2
 *	                                cc      cc -O   gcc     gcc -O2
 *	((x & y) | (~x & z))            8       3       8       3
 *	(z ^ (x & (y ^ z)))             7       3       7       3
 *
 *	((x & y) | (x & z) | (y & z))   11      5       9       4
 *	((x & y) | (z & (x | y)))       9       4       9       4
 */
#define f1(x,y,z) ((x & y) | (~x & z))		/* Rounds  0-19: x ? y : z */
#define f2(x,y,z) (x ^ y ^ z)			/* Rounds 20-39: XOR */
#define f3(x,y,z) ((x & y) | (x & z) | (y & z))	/* Rounds 40-59: majority */
#define f4(x,y,z) (x ^ y ^ z)			/* Rounds 60-79: XOR */

/* The SHA Mysterious Constants */
#define K1	0x5A827999L		/* Rounds  0-19: sqrt(2) * 2^30 */
#define K2	0x6ED9EBA1L		/* Rounds 20-39: sqrt(3) * 2^30 */
#define K3	0x8F1BBCDCL		/* Rounds 40-59: sqrt(5) * 2^30 */
#define K4	0xCA62C1D6L		/* Rounds 60-79: sqrt(10) * 2^30 */

#define ROTL(n,X)	(((X) << n) | ((X) >> (32 - n)))

#define HASH_BUFFER_SIZE 5
#define HASH_EXTRA_SIZE 80

static void
SHATransform (uint32_t digest[85], uint32_t const data[16])
{
	uint32_t A, B, C, D, E;     	/* Local vars */
	uint32_t TEMP;
	uint32_t * W;
	int i;

	/*
	 * Do the preliminary expansion of 16 to 80 words.  Doing it
	 * out-of-line line this is faster than doing it in-line on
	 * register-starved machines like the x86, and not really any
	 * slower on real processors.
	 */
	W = digest + HASH_BUFFER_SIZE;
	/* The next thing is not nice and should really be
	 *	bcopy (data, W, 16 * sizeof (uint32_t));
	 * but Solaris 2.5.1 defined bcopy as [sys/sunddi.h]
	 *	extern void bcopy(caddr_t, caddr_t, size_t);
	 * where 'caddr_t' is 'char *'.
	 * Now it compiles on Solaris 2.5.1 too.
	 */
	bcopy ((void *) data, (void *) W, 16 * sizeof (uint32_t));
	for (i = 0; i < 64; i++) {
		TEMP = W[i] ^ W[i + 2] ^ W[i + 8] ^ W[i + 13];
		W[i + 16] = ROTL (1, TEMP);
	}

	/* Set up first buffer and local data buffer */
	A = digest[0];
	B = digest[1];
	C = digest[2];
	D = digest[3];
	E = digest[4];

	/* Heavy mangling, in 4 sub-rounds of 20 iterations each. */
	for (i = 0; i < 80; i++) {
		if (i < 40) {
			if (i < 20)
				TEMP = f1 (B, C, D) + K1;
			else
				TEMP = f2 (B, C, D) + K2;
		} else {
			if (i < 60)
				TEMP = f3 (B, C, D) + K3;
			else
				TEMP = f4 (B, C, D) + K4;
		}
		TEMP += ROTL (5, A) + E + W[i];
		E = D; D = C; C = ROTL (30, B); B = A; A = TEMP;
	}

	/* Build message digest */
	digest[0] += A;
	digest[1] += B;
	digest[2] += C;
	digest[3] += D;
	digest[4] += E;
}

#undef f1
#undef f2
#undef f3
#undef f4
#undef K1	
#undef K2
#undef K3	
#undef K4	
#undef ROTL
	

/*
 * This function extracts randomness from the "entropy pool", and
 * returns it in a buffer.  This function computes how many remaining
 * bits of entropy are left in the pool, but it does not restrict the
 * number of bytes that are actually obtained.
 */

#if POOLWORDS % 16 != 0
#error extract_entropy() assumes that POOLWORDS is a multiple of 16 words.
#endif

static int
extract_entropy (struct rand_state * r, uio_t * uiop)
{
	uint32_t tmp[HASH_BUFFER_SIZE + HASH_EXTRA_SIZE];
	int i;
	int err;
	int n;

	n = MAX_BEFORE_YIELD;
	while ((uiop->uio_resid > 0) && (n > 0)) {
		/* Hash the pool to get the output */
		tmp[0] = 0x67452301;
		tmp[1] = 0xefcdab89;
		tmp[2] = 0x98badcfe;
		tmp[3] = 0x10325476;
		tmp[4] = 0xc3d2e1f0;
		for (i = 0; i < POOLWORDS; i += 16) {
			SHATransform (tmp, r->pool + i);
		}

		/*
		 * The following code does two separate things that happen
		 * to both work two words at a time, so are convenient
		 * to do together.
		 *
		 * First, this feeds the output back into the pool so
		 * that the next call will return different results.
		 * Any perturbation of the pool's state would do, even
		 * changing one bit, but this mixes the pool nicely.
		 *
		 * Second, this folds the output in half to hide the data
		 * fed back into the pool from the user and further mask
		 * any patterns in the hash output.  (The exact folding
		 * pattern is not important; the one used here is quick.)
		 *
		 * Fold the middle word by half and put result in upper
		 * and lower half (byte order independent).
		 */

		add_entropy_words (r, tmp[0], tmp[3]); tmp[0] ^= tmp[3];
		add_entropy_words (r, tmp[1], tmp[4]); tmp[1] ^= tmp[4];
		tmp[2] ^= (tmp[2] << 16) ^ (tmp[2] >> 16);

		/* Copy data to destination */
		err = uiomove ((caddr_t) tmp,
			       HASH_BUFFER_SIZE * (sizeof (uint32_t) / 2),
			       UIO_READ, uiop);

		if (err != 0) {
			return (err);
		}

		n --;
	}

	/* Wipe data just returned from memory */
	/* The next thing is not nice and should really be
	 *	bzero (tmp, sizeof(tmp));
	 * but Solaris 2.5.1 defined bzero as [sys/sunddi.h]
	 *	extern void bzero(caddr_t, size_t);
	 * where 'caddr_t' is 'char *'.
	 * Now it compiles on Solaris 2.5.1 too.
	 */
	bzero ((void *) tmp, sizeof(tmp));

	return (0);
}


