From daw@cs.berkeley.edu Fri Apr 26 22:04:22 PDT 1996
Article: 416 of isaac.lists.coderpunks
Path: news.isaac.cs.berkeley.edu!not-for-mail
From: daw@cs.berkeley.edu (David Wagner)
Newsgroups: isaac.lists.coderpunks
Subject: Re: Distilled Wisdom for Cryptograph Random Sources
Date: 8 Apr 1996 12:58:51 -0700
Organization: ISAAC Group, UC Berkeley
Lines: 66
Sender: bin@abraham.cs.berkeley.edu
Approved: mail2news@news.isaac.cs.berkeley.edu
Distribution: isaac
Message-ID: <4kblk7$he@joseph.cs.berkeley.edu>
References: <199604070041.QAA28730@netcom9.netcom.com>
NNTP-Posting-Host: abraham.cs.berkeley.edu
Precedence: bulk

In article <199604070041.QAA28730@netcom9.netcom.com>,
Bill Frantz <frantz@netcom.com> wrote:
> MD5hash myRandPool;
> 
> addEntropy(Time eventTime) {
>    MD5 md5 = new MD5();
>    md5.update(myRandPool);
>    md5.update(eventTime);
>    myRandPool = md5.final();
> }
> 
> RandData genRand() {
>    RandData temp;
>    MD5 md5 = new MD5();
>    md5.update(myRandPool);
>    md5.update(currentTime);
>    temp = md5.final();
>    MD5 md5 = new MD5();
>    md5.update(temp);
>    md5.update(currentTime);
>    myRandPool = md5.final();
>    return temp;
> }

If I understand this correctly, this is absolutely horrible.  The state
of the RandPool is updated as a known function of the previous PRNG
output: so given one known PRNG output, I can recover all the following
PRNG outputs.  Eek!

Here's my straw man proposal; I haven't analyzed it carefully, but it
avoids a lot of the common potential weaknesses, I think.  [Excuse my
pseudo-Java: I don't know Java at all.]

/* secret global variables */
/* it's crucial that these two have a fixed length */
byte myRandPool[16]; /* pool of entropy */
long counter;

addEntropy(byte buf[]) { /* crunch in some more entropy */
	MD5 md5 = new MD5();
	md5.update("addEntropy() cruncher"); /* for slightly more robust security */
	md5.update(myRandPool);
	md5.update(buf);
	myRandPool = md5.final();
	/* really should have a conservative entropy estimator here */
}

typedef byte randOutput[4];

/* don't call this until you're *sure* you've crunched in enough entropy */
randOutput genRand() { /* generate & output some PRNG bytes */
	MD5 md5 = new MD5();
	long x;
	/* we probably should refuse to generate output unless we've estimated
	   that "enough" entropy has been crunched in, to be idiot-proof. */
	md5.update("genRand() prng"); /* for slightly more robust security */
	md5.update(myRandPool);
	atomic { x = ++counter; };  /* this line had better be atomic!!! */
	md5.update(x);
	return first4bytes(md5.final()); /* for slightly more robust security */
}

Note the md5.update() with a string describing the function's use of MD5:
this essentially gives you two different independent hash functions, h()
and h'(); using an independent hash for each logically different usage
seems more robust to design failures, IMHO.


