/* crypto/rand/md5_rand.c */
/* Copyright (C) 1995-1997 Eric Young (eay@mincom.oz.au)
 * All rights reserved.
 *
 * This package is an SSL implementation written
 * by Eric Young (eay@mincom.oz.au).
 * The implementation was written so as to conform with Netscapes SSL.
 * 
 * This library is free for commercial and non-commercial use as long as
 * the following conditions are aheared to.  The following conditions
 * apply to all code found in this distribution, be it the RC4, RSA,
 * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
 * included with this distribution is covered by the same copyright terms
 * except that the holder is Tim Hudson (tjh@mincom.oz.au).
 * 
 * Copyright remains Eric Young's, and as such any Copyright notices in
 * the code are not to be removed.
 * If this package is used in a product, Eric Young should be given attribution
 * as the author of the parts of the library used.
 * This can be in the form of a textual message at program startup or
 * in documentation (online or textual) provided with the package.
 * 
 * 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 copyright
 *    notice, this list of conditions and the following disclaimer.
 * 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. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *    "This product includes cryptographic software written by
 *     Eric Young (eay@mincom.oz.au)"
 *    The word 'cryptographic' can be left out if the rouines from the library
 *    being used are not cryptographic related :-).
 * 4. If you include any Windows specific code (or a derivative thereof) from 
 *    the apps directory (application code) you must include an acknowledgement:
 *    "This product includes software written by Tim Hudson (tjh@mincom.oz.au)"
 * 
 * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``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 OR CONTRIBUTORS 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.
 * 
 * The licence and distribution terms for any publically available version or
 * derivative of this code cannot be changed.  i.e. this code cannot simply be
 * copied and put under another distribution licence
 * [including the GNU Public Licence.]
 */

#include <stdio.h>
#include "cryptlib.h"
#include <sys/types.h>
#include <time.h>
#ifdef NO_MD5
#include <md5.h>
#else
#include "md5.h"
#endif
#include "rand.h"

/* Changed how the state buffer used.  I now attempt to 'wrap' such
 * that I don't run over the same locations the next time  go through
 * the 1023 bytes - many thanks to
 * Robert J. LeBlanc <rjl@renaissoft.com> for his comments
 */

/*#define NORAND	1 */
/*#define PREDICT	1 */

#define STATE_SIZE	1023
static int state_num=0,state_index=0;
static unsigned char state[STATE_SIZE];
static unsigned char md[MD5_DIGEST_LENGTH];
static int count=0;

char *RAND_version="RAND part of SSLeay 0.6.6 14-Jan-1997";

void RAND_cleanup()
	{
	memset(state,0,STATE_SIZE);
	state_num=0;
	state_index=0;
	memset(md,0,MD5_DIGEST_LENGTH);
	count=0;
	}

void RAND_seed(buf,num)
unsigned char *buf;
int num;
	{
	int i,j,k,st_idx,st_num;
	MD5_CTX m;

#ifdef NORAND
	return;
#endif

	CRYPTO_w_lock(CRYPTO_LOCK_RAND);
	st_idx=state_index;
	st_num=state_num;

	state_index=(state_index+num);
	if (state_index > STATE_SIZE)
		{
		state_index%=STATE_SIZE;
		state_num=STATE_SIZE;
		}
	else if (state_num < STATE_SIZE)	
		{
		if (state_index > state_num)
			state_num=state_index;
		}
	CRYPTO_w_unlock(CRYPTO_LOCK_RAND);

	for (i=0; i<num; i+=MD5_DIGEST_LENGTH)
		{
		j=(num-i);
		j=(j > MD5_DIGEST_LENGTH)?MD5_DIGEST_LENGTH:j;

		MD5_Init(&m);
		MD5_Update(&m,md,MD5_DIGEST_LENGTH);
		k=(st_idx+j)-STATE_SIZE;
		if (k > 0)
			{
			MD5_Update(&m,&(state[st_idx]),j-k);
			MD5_Update(&m,&(state[0]),k);
			}
		else
			MD5_Update(&m,&(state[st_idx]),j);
			
		MD5_Update(&m,buf,j);
		MD5_Final(md,&m);

		buf+=j;

		for (k=0; k<j; k++)
			{
			state[st_idx++]^=md[k];
			if (st_idx >= STATE_SIZE)
				{
				st_idx=0;
				st_num=STATE_SIZE;
				}
			}
		}
	memset((char *)&m,0,sizeof(m));
	}

void RAND_bytes(buf,num)
unsigned char *buf;
int num;
	{
	int i,j,k,st_num,st_idx;
	MD5_CTX m;
	static int init=1;
	unsigned long l;
#ifdef DEVRANDOM
	FILE *fh;
#endif

#ifdef PREDICT
	{
	static unsigned char val=0;

	for (i=0; i<num; i++)
		buf[i]=val++;
	return;
	}
#endif

	CRYPTO_w_lock(CRYPTO_LOCK_RAND);

	if (init)
		{
		init=0;
		CRYPTO_w_unlock(CRYPTO_LOCK_RAND);
		/* put in some default random data, we need more than
		 * just this */
		RAND_seed((unsigned char *)&m,sizeof(m));
#ifndef MSDOS
		l=getpid();
		RAND_seed((unsigned char *)&l,sizeof(l));
		l=getuid();
		RAND_seed((unsigned char *)&l,sizeof(l));
#endif
		l=time(NULL);
		RAND_seed((unsigned char *)&l,sizeof(l));

#ifdef DEVRANDOM
		/* 
		 * Use a random entropy pool device.
		 * Linux 1.3.x and FreeBSD-Current has 
		 * this. Use /dev/urandom if you can
		 * as /dev/random will block if it runs out
		 * of random entries.
		 */
		if ((fh = fopen(DEVRANDOM, "r")) != NULL)
			{
			unsigned char buf[32];

			fread((unsigned char *)buf,1,32,fh);
			/* we don't care how many bytes we read,
			 * we will just copy the 'stack' if there is
			 * nothing else :-) */
			fclose(fh);
			RAND_seed(buf,32);
			memset(buf,0,32);
			}
#endif
#ifdef PURIFY
		memset(state,0,STATE_SIZE);
		memset(md,0,MD5_DIGEST_LENGTH);
#endif
		CRYPTO_w_lock(CRYPTO_LOCK_RAND);
		}

	st_idx=state_index;
	st_num=state_num;
	state_index+=num;
	if (state_index > state_num)
		state_index=(state_index%state_num);

	CRYPTO_w_unlock(CRYPTO_LOCK_RAND);

	while (num > 0)
		{
		j=(num >= MD5_DIGEST_LENGTH/2)?MD5_DIGEST_LENGTH/2:num;
		num-=j;
		MD5_Init(&m);
		MD5_Update(&m,&(md[MD5_DIGEST_LENGTH/2]),MD5_DIGEST_LENGTH/2);
#ifndef PURIFY
		MD5_Update(&m,buf,j); /* purify complains */
#endif
		k=(st_idx+j)-st_num;
		if (k > 0)
			{
			MD5_Update(&m,&(state[st_idx]),j-k);
			MD5_Update(&m,&(state[0]),k);
			}
		else
			MD5_Update(&m,&(state[st_idx]),j);
		MD5_Final(md,&m);

		for (i=0; i<j; i++)
			{
			if (st_idx >= st_num)
				st_idx=0;
			state[st_idx++]^=md[i];
			*(buf++)=md[i+MD5_DIGEST_LENGTH/2];
			}
		}

	MD5_Init(&m);
	MD5_Update(&m,(unsigned char *)&count,sizeof(count)); count++;
	MD5_Update(&m,md,MD5_DIGEST_LENGTH);
	MD5_Final(md,&m);
	memset(&m,0,sizeof(m));
	}

#ifdef WINDOWS
#include <windows.h>
#include <rand.h>

/*****************************************************************************
 * Initialisation function for the SSL random generator.  Takes the contents
 * of the screen as random seed.
 *
 * Created 960901 by Gertjan van Oosten, gertjan@West.NL, West Consulting B.V.
 *
 * Code adapted from
 * <URL:http://www.microsoft.com/kb/developr/win_dk/q97193.htm>;
 * the original copyright message is:
 *
//   (C) Copyright Microsoft Corp. 1993.  All rights reserved.
//
//   You have a royalty-free right to use, modify, reproduce and
//   distribute the Sample Files (and/or any modified version) in
//   any way you find useful, provided that you agree that
//   Microsoft has no warranty obligations or liability for any
//   Sample Application Files which are modified.
 */
/*
 * I have modified the loading of bytes via RAND_seed() mechanism since
 * the origional would have been very very CPU intensive since RAND_seed()
 * does an MD5 per 16 bytes of input.  The cost to digest 16 bytes is the same
 * as that to digest 56 bytes.  So under the old system, a screen of
 * 1024*768*256 would have been CPU cost of approximatly 49,000 56 byte MD5
 * digests or digesting 2.7 mbytes.  What I have put in place would
 * be 48 16k MD5 digests, or efectivly 48*16+48 MD5 bytes or 816 kbytes
 * or about 3.5 times as much.
 * - eric 
 */
void RAND_screen(void)
{
  HDC		hScrDC;		/* screen DC */
  HDC		hMemDC;		/* memory DC */
  HBITMAP	hBitmap;	/* handle for our bitmap */
  HBITMAP	hOldBitmap;	/* handle for previous bitmap */
  BITMAP	bm;		/* bitmap properties */
  unsigned int	size;		/* size of bitmap */
  char		*bmbits;	/* contents of bitmap */
  int		w;		/* screen width */
  int		h;		/* screen height */
  int		y;		/* y-coordinate of screen lines to grab */
  int		n = 16;		/* number of screen lines to grab at a time */

  /* Create a screen DC and a memory DC compatible to screen DC */
  hScrDC = CreateDC("DISPLAY", NULL, NULL, NULL);
  hMemDC = CreateCompatibleDC(hScrDC);

  /* Get screen resolution */
  w = GetDeviceCaps(hScrDC, HORZRES);
  h = GetDeviceCaps(hScrDC, VERTRES);

  /* Create a bitmap compatible with the screen DC */
  hBitmap = CreateCompatibleBitmap(hScrDC, w, n);

  /* Select new bitmap into memory DC */
  hOldBitmap = SelectObject(hMemDC, hBitmap);

  /* Get bitmap properties */
  GetObject(hBitmap, sizeof(BITMAP), (LPSTR)&bm);
  size = (unsigned int)bm.bmWidthBytes * bm.bmHeight * bm.bmPlanes;

  bmbits = malloc(size);
  if (bmbits) {
    /* Now go through the whole screen, repeatedly grabbing n lines */
    for (y = 0; y < h-n; y += n)
    	{
	unsigned char md[MD5_DIGEST_LENGTH];

	/* Bitblt screen DC to memory DC */
	BitBlt(hMemDC, 0, 0, w, n, hScrDC, 0, y, SRCCOPY);

	/* Copy bitmap bits from memory DC to bmbits */
	GetBitmapBits(hBitmap, size, bmbits);

	/* Get the MD5 of the bitmap */
	MD5(bmbits,size,md);

	/* Seed the random generator with the MD5 digest */
	RAND_seed(md, MD5_DIGEST_LENGTH);
	}

    free(bmbits);
  }

  /* Select old bitmap back into memory DC */
  hBitmap = SelectObject(hMemDC, hOldBitmap);

  /* Clean up */
  DeleteObject(hBitmap);
  DeleteDC(hMemDC);
  DeleteDC(hScrDC);
}
#endif
