freeswitch/libs/libzrtp/third_party/bnlib/test/random.c

302 lines
8.3 KiB
C

/*
* Copyright (c) 1993, 1994 Colin Plumb. All rights reserved.
* For licensing and other legal details, see the file legal.c.
*
* Cryptographic random number generation.
*/
#include "first.h"
#include <string.h>
#include "kb.h" /* For kbGet() other stuff */
#include "md5.h"
#include "noise.h"
#include "random.h"
#include "randpool.h"
#include "userio.h"
#include "kludge.h"
/*
* This code uses the randpool.c code to generate random numbers.
* That can be augmented with other techniques, such as the
* ANSI X9.17 generator, but the X9.17 generator uses a key-generating
* key which needs to be obtained from somewhere, and the location is
* not entirely clear. The randpool.c functions are entirely
* adequate; extra layers are for belt-and-suspenders security and
* compliance to standards.
*
* For generating long-lived secret keys, we go one more step:
* actually keep track of (an estimate of) the amount of entropy
* which is in the random number pool, and wait for events until
* the amount of entropy accumulated is enough to make all of the
* bits of the secret key truly random. Of course, the guarantees
* of cryptographic strength still apply even if this estimation
* is faulty.
*/
/* Get some random bytes */
void
randBytes(byte *buf, unsigned len)
{
randPoolGetBytes(buf, len);
}
/*
* A handy utility for generating uniformly distributed random numbers
* in a small range.
*/
unsigned
randRange(unsigned range)
{
unsigned div, r;
byte b[2];
if (range <= 1)
return 0;
if (range <= 256) {
div = 256/range;
do {
randBytes(b, 1);
r = b[0]/div;
} while (r >= range);
} else {
div = (unsigned)(65536/range);
do {
randBytes(b, 2);
r = ((unsigned)b[0] << 8 | b[1])/div;
} while (r >= range);
}
b[0] = b[1] = 0;
return r;
}
#ifdef UNIX /* Or we have popen() */
/*
* Execute the command "string", adding the entropy from the data thus
* gethered to the random number pool. Because the pool is rather
* slow and we want to encourage the use of lots of data, rather than
* adding the data directly, the MD5 is taken and that is added to the
* pool.
*/
int
randSourceSet(char const *string, unsigned len, int pri)
{
FILE *f;
struct MD5Context md5;
char buf[256];
int i;
(void)len; /* string is null-terminated */
(void)pri; /* Use every argument, regardless of priority */
f = popen(string, "r");
if (!f)
return -1;
MD5Init(&md5);
while ((i = fread(buf, 1, sizeof(buf), f)) > 0)
MD5Update(&md5, (unsigned char *)buf, i);
pclose(f);
MD5Final((unsigned char *)buf, &md5);
randPoolAddBytes((unsigned char *)buf, 16);
memset(buf, 0, sizeof(buf));
return 0;
}
#endif
/*
* True random bit handling
*/
/*
* Truly random bits are difficult to get and must be carefully hoarded.
* These functions use the randpool.c code to store the entropy, and provide
* some bookkeeping on the count of bits of true (Shannon) entropy available
* in the pool.
*
* For generating ordinary session keys, "as much entropy as you've got"
* is good enough, and no accounting is done, except to get some entropy
* to generate the random number seed file if necessary.
*
* But for generating long-lived secret key components, extraordinary
* measures are called for. In addition to what may have been available
* from the random seed file, random data from timed keystrokes is
* accumulated until enough is available.
*
* An estimate of the number of bits of true (Shannon) entropy in the pool
* is kept in trueRandBits. This is incremented when timed keystrokes
* are available, and decremented when bits are explicitly consumed for
* some purpose or another. This counter is maintained here, scaled by
* FRACBITS to count fractional bits for thoroughness. (Thus, the name
* "trueRandBits" is a bit misleading, since it actually counts sixteenths
* of a bit, but I can't think of a better one.)
*
* randFlush is the pool-stirring function. It is also called to
* obliterate traces of old random bits after prime generation is
* completed. (Primes are the most carefully-guarded values in PGP.)
*/
#define FRACBITS 4
#define DERATING 0x28 /* 2.5 bits subtracted for derating */
static word32 trueRandBits = 0; /* Bits of entropy in pool */
/*
* Ensure that the random numbers generated by prior calls to randBytes
* will never be recoverable from the contents of memory. This doesn't
* wipe memory to a fixed value (the entropy might come in handy for future
* operations), it just runs the generators forward enough that the previous
* state is irretrievable.
*
* This is called after prime generation, before the random data is saved
* out, so it is protecting prime data and is particularly paranoid.
*/
void
randFlush(void)
{
byte buf[16];
int i;
for (i = 0; i < 3; i++) /* Zipper + Belt + Suspenders */
randPoolStir(); /* Clean pseudo-random generator */
memset(buf, 0, sizeof(buf));
trueRandBits = 0;
}
/*
* Given an event (typically a keystroke) coded by "event" at a random time,
* add all randomness to the random pool, compute a (conservative) estimate
* of the amount, add it to the pool, and return the amount of randomness.
* (The return value is just for informational purposes.)
*
* Double events are okay, but three in a row is considered
* suspicious and the randomness is counted as 0.
*
* As an extra precaution against key repeat or other very regular input
* data, the entropy extimate is derived not from the time interval measured,
* but from the minimum of it and the (absolute) difference between it and
* the previous time interval, i.e. the second-order delta.
*/
unsigned
randEvent(int event)
{
static int event1 = 0, event2 = 0; /* Previous events */
static word32 prevdelta; /* Previous delta */
word32 delta; /* Time between last two events */
unsigned cbits; /* Entropy estimate, in bits. */
word32 t; /* Temprary value */
int i;
delta = noise();
randPoolAddBytes((byte *)&event, sizeof(event));
/*
* Don't credit triple events with any entropy on the grounds that
* they're probably something periodic like key repeat. But remember
* the delta.
*/
if (event == event1 && event == event2) {
prevdelta = delta;
return 0;
}
event2 = event1;
event1 = event;
/* Compute second-order delta */
t = (delta > prevdelta) ? delta - prevdelta : prevdelta - delta;
/* Remember current delta for next time */
prevdelta = delta;
/* Find minimum of delta and second-order delta */
if (delta > t)
delta = t;
/* Avoid divide-by-zero errors below */
if (!delta)
return 0;
/* Count the number of bits of entropy available - integer log2. */
cbits = 0;
i = 16;
t = 0xffffffff;
do {
t <<= i;
if (delta & t)
cbits += i;
else
delta <<= i;
} while (i >>= 1);
/*
* At this point, delta is normalized and has its high bit set.
* Now count fractional bits, using binary logarithm algorithm
*/
for (i = 0; i < FRACBITS; i++) {
cbits <<= 1;
delta >>= 16;
delta *= delta;
if (delta & 0x80000000)
cbits++;
else
delta <<= 1;
}
if (cbits <= DERATING)
return 0; /* nothing */
cbits -= DERATING;
trueRandBits += cbits;
if (trueRandBits > RANDPOOLBITS<<FRACBITS)
trueRandBits = RANDPOOLBITS<<FRACBITS;
return cbits;
}
/*
* Performs an accumulation of random bits. As long as there are
* fewer bits in the buffer than are needed, prompt for more.
* (kbGet is known to call randEvent() which increments trueRandBits.)
*/
void
randAccum(unsigned count)
{
word32 randbits = trueRandBits;
noise(); /* Establish a baseline for timing comparisons */
if (count > RANDPOOLBITS)
count = RANDPOOLBITS;
if (randbits>>FRACBITS >= count)
return;
userPrintf("\n\
We need to generate %u random bits. This is done by measuring the\n\
time intervals between your keystrokes. Please enter some random text\n\
on your keyboard until you hear the beep:\n", count - (randbits>>FRACBITS));
kbCbreak();
do {
/* display counter to show progress */
userPrintf(("\r%4u "), count-(unsigned)(randbits>>FRACBITS));
userFlush(); /* ensure screen update */
kbFlush(0); /* Typeahead is illegal */
(void)kbGet(); /* Wait for next char */
/* Print flag indicating acceptance (or not) */
userPutc(trueRandBits == randbits ? '?' : '.');
randbits = trueRandBits;
} while (randbits>>FRACBITS < count);
/* Do final display update */
userPuts(("\r 0 *"));
userPuts("\a -Enough, thank you.\n");
/* Do an extra-thorough flush to absorb extra typing. */
kbFlush(1);
kbNorm();
}