blob: 2617b5e858c111b6c71cdfe176bb1416643c9f28 [file] [log] [blame]
/*
* Physically random numbers (very nearly uniform)
* D. P. Mitchell
* Modified by Matt Blaze 7/95
*/
/*
* The authors of this software are Don Mitchell and Matt Blaze.
* Copyright (c) 1995 by AT&T.
* Permission to use, copy, and modify this software without fee
* is hereby granted, provided that this entire notice is included in
* all copies of any software which is or includes a copy or
* modification of this software and in all copies of the supporting
* documentation for such software.
*
* This software may be subject to United States export controls.
*
* THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR AT&T MAKE ANY
* REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
* OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
*/
/*
* WARNING: depending on the particular platform, raw_truerand()
* output may be biased or correlated. In general, you can expect
* about 16 bits of "pseudo-entropy" out of each 32 bit word returned
* by truerand(), but it may not be uniformly diffused. You should
* raw_therefore run the output through some post-whitening function
* (like MD5 or DES or whatever) before using it to generate key
* material. (RSAREF's random package does this for you when you feed
* raw_truerand() bits to the seed input function.)
*
* The application interface, for 8, 16, and 32 bit properly "whitened"
* random numbers, can be found in trand8(), trand16(), and trand32().
* Use those instead of calling raw_truerand() directly.
*
* The basic idea here is that between clock "skew" and various
* hard-to-predict OS event arrivals, counting a tight loop will yield
* a little (maybe a third of a bit or so) of "good" randomness per
* interval clock tick. This seems to work well even on unloaded
* machines. If there is a human operator at the machine, you should
* augment truerand with other measure, like keyboard event timing.
* On server machines (e.g., when you need to generate a
* Diffie-Hellman secret) truerand alone may be good enough.
*
* Test these assumptions on your own platform before fielding a
* system based on this software or these techniques.
*
* This software seems to work well (at 10 or so bits per
* raw_truerand() call) on a Sun Sparc-20 under SunOS 4.1.3 and on a
* P100 under BSDI 2.0. You're on your own elsewhere.
*
*/
#include "t_defines.h"
#ifdef WIN32
# ifdef CRYPTOLIB
/* Cryptolib contains its own truerand() on both UNIX and Windows. */
/* Only use cryptolib's truerand under Windows */
# include "libcrypt.h"
unsigned long
raw_truerand()
{
return truerand();
}
# else /* !CRYPTOLIB && WIN32 */
#include <wtypes.h>
#include <winbase.h>
#include <windef.h>
#include <winnt.h>
#include <winuser.h>
#include <process.h>
volatile unsigned long count, ocount, randbuf;
volatile int dontstop;
char outbuf[1024], *bufp;
static void counter() {
while (dontstop)
count++;
_endthread();
}
static unsigned long roulette() {
unsigned long thread;
count = 0;
dontstop= 1;
while ((thread = _beginthread((void *)counter, 1024, NULL)) < 0)
;
Sleep(16);
dontstop = 0;
Sleep(1);
count ^= (count>>3) ^ (count>>6) ^ (ocount);
count &= 0x7;
ocount = count;
randbuf = (randbuf<<3) ^ count;
return randbuf;
}
unsigned long
raw_truerand() {
roulette();
roulette();
roulette();
roulette();
roulette();
roulette();
roulette();
roulette();
roulette();
roulette();
return roulette();
}
# endif /* CRYPTOLIB */
#else /* !WIN32 */
#include <signal.h>
#include <setjmp.h>
#include <sys/time.h>
#include <math.h>
#include <stdio.h>
#ifdef OLD_TRUERAND
static jmp_buf env;
#endif
static unsigned volatile count
#ifndef OLD_TRUERAND
, done = 0
#endif
;
static unsigned ocount;
static unsigned buffer;
static void
tick()
{
struct itimerval it, oit;
it.it_interval.tv_sec = 0;
it.it_interval.tv_usec = 0;
it.it_value.tv_sec = 0;
it.it_value.tv_usec = 16665;
if (setitimer(ITIMER_REAL, &it, &oit) < 0)
perror("tick");
}
static void
interrupt()
{
if (count) {
#ifdef OLD_TRUERAND
longjmp(env, 1);
#else
++done;
return;
#endif
}
(void) signal(SIGALRM, interrupt);
tick();
}
static unsigned long
roulette()
{
#ifdef OLD_TRUERAND
if (setjmp(env)) {
count ^= (count>>3) ^ (count>>6) ^ ocount;
count &= 0x7;
ocount=count;
buffer = (buffer<<3) ^ count;
return buffer;
}
#else
done = 0;
#endif
(void) signal(SIGALRM, interrupt);
count = 0;
tick();
#ifdef OLD_TRUERAND
for (;;)
#else
while(done == 0)
#endif
count++; /* about 1 MHz on VAX 11/780 */
#ifndef OLD_TRUERAND
count ^= (count>>3) ^ (count>>6) ^ ocount;
count &= 0x7;
ocount=count;
buffer = (buffer<<3) ^ count;
return buffer;
#endif
}
unsigned long
raw_truerand()
{
count=0;
(void) roulette();
(void) roulette();
(void) roulette();
(void) roulette();
(void) roulette();
(void) roulette();
(void) roulette();
(void) roulette();
(void) roulette();
(void) roulette();
return roulette();
}
int
raw_n_truerand(n)
int n;
{
int slop, v;
slop = 0x7FFFFFFF % n;
do {
v = raw_truerand() >> 1;
} while (v <= slop);
return v % n;
}
#endif /* !CRYPTOLIB || !WIN32 */