--- pgp/src/random.c 2018/04/24 16:39:11 1.1.1.4 +++ pgp/src/random.c 2018/04/24 16:44:31 1.1.1.8 @@ -1,739 +1,550 @@ -/********************************************************************** - random.c - C source code for random number generation - 19 Nov 86 - (c) Copyright 1986 by Philip Zimmermann. All rights reserved. - - Revised Jul 88 by PRZ and again Dec 88 by Allan Hoeltje - to use IBM PC 8253 timer0 for a faster counter. - Revised Apr 89 by PRZ to recycle random bytes. - Revised 29 Jul 91 by PRZ for use in more limited environments. - Later revised by several other folks to run in difficult environments - such as Unix, VAX/VMS and others. - Revised 2 Sep 92 by Peter Gutmann and PRZ to use MD5 to distill - high quality randomness down from low-grade random noise. - Revised 25 Feb 93 by Colin Plumb to use MD5 much more for - more secure random numbers. (randstir hacked) - Revised 4 Mar 93 by Colin Plumb to remove the CRUDE and non-USE_MD5 - options, as no platform currently uses them, they make the code - hard to read, and they were suffering bit rot. Also added - NO_ITIMER for the isc port and randaccum_later. - - This code generates truly random numbers derived from a counter that is - incremented continuously while the keyboard is scanned for user input. - Every time the user touches a key, the least significant bits of the - counter are pushed on a stack. Later, this supply of random bytes can - be popped off the stack by applications requiring stochastic numbers. - Cryptographic applications require this kind of randomness. - - The only requirement to make this work is that keypress must be called - frequently, and/or getkey must be called to read the keyboard. - - Note that you can only get as many random bytes as the number of - bytes accumulated by the user touching the keyboard. -**********************************************************************/ - -#ifdef UNIX -#include -#include +/* + * True and cryptographic random number generation. + * + * (c) Copyright 1990-1994 by Philip Zimmermann. All rights reserved. + * The author assumes no liability for damages resulting from the use + * of this software, even if the damage results from defects in this + * software. No warranty is expressed or implied. + * + * Note that while most PGP source modules bear Philip Zimmermann's + * copyright notice, many of them have been revised or entirely written + * by contributors who frequently failed to put their names in their + * code. Code that has been incorporated into PGP from other authors + * was either originally published in the public domain or is used with + * permission from the various authors. + * + * PGP is available for free to the public under certain restrictions. + * See the PGP User's Guide (included in the release package) for + * important information about licensing, patent restrictions on + * certain algorithms, trademarks, copyrights, and export controls. + * + * Written by Colin Plumb. + */ -#ifdef NO_ITIMER /* Some systems lie about itimer */ -#undef ITIMER_REAL -#endif +#include +#include +#include +#include +#include /* For SIGINT */ +#include -#ifndef ITIMER_REAL -#include -#endif -#endif /* UNIX */ +#include "system.h" /* For ttycbreak, getch, etc. */ +#include "idea.h" +#include "md5.h" +#include "noise.h" +#include "language.h" +#include "random.h" +#include "fileio.h" /* For FOPRBIN */ +#include "pgp.h" /* For globalRandseedName */ +#include "randpool.h" -#if defined(UNIX) || defined(VMS) -#include -#endif +/* + * As of PGP 2.6.2, the randseed.bin file has been expanded. An explanation + * of how the whole thing works in in order, as people are always suspiscious + * of the random number generator. (After the xorbytes bug in 2.6, perhaps + * you should be.) There are two random number generators in PGP. One + * is the "cryptRand" family, which is based on X9.17, but uses IDEA instead + * of 2-key EDE triple-DES. This is the generator with a lot of peer review. + * The implementation is in idea.c. + * The second is the "trueRand" family, which attempt to accurately measure + * the entropy available from keyboard I/O. It keeps a lot more state. + * The implementation of this is in randpool.c. + * Originally, the trueRand generator was only used for generating primes, + * and the cryptRand for generating IDEA session keys. But things have + * become a bit more complex. In particular, the X9.17 specification + * wants a source of high-resolution time-of-day information, as a source + * of some "true" randomness to throw in. So we use the trueRand pool + * for that. + * The cryptRand functions keep a state file around, usually named + * randseed.bin, for a seed, as the X9.17 generator requires 24 bytes of + * known initial information. + * This data in this file is carefully "washed" before and after use to + * help ensure that if the file is captured or altered, the keys will + * not be too vulnerable. A washing consists of an encryption in PGP's + * usual CFB mode of the material coming from or being written to the + * randseed.bin file on disk. Assuming the cipher is strong, the effects + * of the wash are as difficult to predict as the key that is used is + * difficult to guess. + * Beforehand, we use the MD5 of the file being encrypted as an additional + * source of randomness (on the theory that an attacker trying to break + * a session key probably doesn't have the plaintext, or he wouldn't need + * to bother), and use that as an IDEA key (with a fixed IV of zero) + * to encrypt the randseed.bin data. + * After generating an IDEA key and IV, some more random bytes are generated + * to reinitialize randseed.bin, and these are encrypted in the same manner + * as the PGP message before being written to disk, on the assumption that + * if an attacker can decrypt that, they can decrypt the message directly + * and not bother attacking the randseed.bin file. + * The previous code only saved the 24 bytes needed by the X9.17 algorithm. + * But in 2.6.2, we decided to make the randseed.bin file substantially + * larger to hold more information that a would-be attacker must guess. + * There are two reasons for this: + * - Every time you run PGP, especially when responding to one of PGP's + * prompts, PGP samples the keystrokes for use as random numbers. + * It is a shame to throw this entropy (randomness) away just because + * there is no need for it in the current invocation of PGP. + * - A feature was added to 2.6.2 to generate files full of random bytes + * for other programs to use as key material. In this case, we haven't + * got a message we're encrypting to take some entropy from, and we may + * be asked to generate more than 24 random bytes, so there should be + * more than 24 bytes of seed material to work from. + * The implementation is added on to the previous one, to offer assurance + * that it is no weaker. + * When the cryptRand generator is opened, the file is washed (if possible) + * and the first 24 bytes are fed to the cryptographic RNG, while the + * remainder is added to the trueRand random number pool. + * When saving, the randseed.bin file is refilled with newly generated + * bytes, again washed if possible. It turns out (if you study the + * X9.17 RNG) that each of the 2^64 possible timestamp information + * values used in generating each 8 bytes of output generates a output + * value, so the entropy in the trueRand pool is put to good use; this + * is not just generating more data from 24 bytes of seed. + * The random pool is opened and saved with a washing key when + * generating a session key (see make_random_ideakey in crypto.c), + * but it is also opened (harmless if alreasy open) and saved + * (harmless if already saved) without a washing key in the exitPGP routine, + * to mix in any entropy collected in this invocation of PGP even if + * a session key was not generated. + */ -#include /* for putchar() and printf() */ -#include -#ifndef _BSD -#include -#endif -#include "system.h" -#include "random.h" -#include "language.h" +/* + * The new randseed size, big enough to hold the full context of the cryptRand + * and trueRand generators. With the current RANDPOOLBITS of 3072 (384 bytes), + * that's 408 bytes. It's useless to make it any larger, although if + * RANDPOOLBITS is increased, it might be an idea to keep this smaller than + * one disk block on all systems (512 bytes is a good figure to use) + * so we don't change the space requirements for randseed.bin. + */ +#define RANDSEED_BYTES (RANDPOOLBITS/8 + 24) +/* Have we read in the randseed.bin file? */ +static boolean randSeedOpen = 0; +static struct IdeaRandContext randContext; -static long pseudorand(void); /* 31-bit LCG pseudorandom generator */ +/* + * Load the RNG state from the randseed.bin file on disk. + * Returns 0 on success, <0 on error. + * + * If cfb is non-zero, prewashes the data by encrypting with it. + */ +int +cryptRandOpen(struct IdeaCfbContext *cfb) +{ + byte buf[256]; + int len; + FILE *f; + + if (randSeedOpen) + return 0; /* Already open */ + + f = fopen(globalRandseedName, FOPRBIN); + if (!f) + return -1; + + /* First get the bare minimum 24 bytes we need for the IDEA RNG */ + len = fread((char *)buf, 1, 24, f); + if (cfb) + ideaCfbEncrypt(cfb, buf, buf, 24); + ideaRandInit(&randContext, buf, buf+16); + randSeedOpen = TRUE; + if (len != 24) { /* Error */ + fclose(f); + return -1; + } -#ifdef M_XENIX -long time(); -#endif -#ifdef UNIX -#define clock Clock -#endif + /* Read any extra into the random pool */ + for (;;) { + len = fread((char *)buf, 1, sizeof(buf), f); + if (len <= 0) + break; + if (cfb) + ideaCfbEncrypt(cfb, buf, buf, len); + randPoolAddBytes(buf, len); + } -/* pseudorand() is used mainly for debugging while porting to a new - machine-- it makes reproducible sequences, which makes debugging - easier. To use it as the source of random numbers, define - PSEUDORANDOM. Warning: Don't do this for actual applications. -*/ -/** - ** Minimal Standard Pseudo-Random Number Generator - ** - ** Author: Fuat C. Baran, Columbia University, 1988 - ** - ** Based on code in "Random Number Generators: Good Ones are Hard to Find", - ** by Stephen K. Park and Keith W. Miller in Communications of the ACM, - ** 31, 10 (Oct. 1988) pp. 1192-1201. - ** - **/ - -/* some constants we need */ -#define A 16807L -#define M 2147483647L /* Mersenne prime 2^31 -1 */ -#define Q 127773L /* M div A (M / A) */ -#define R 2836L /* M mod A (M % A) */ - -static long pseudorand(void) -{ - long hi, lo; -#ifdef DEBUG - static long seed = 1; -#else - static long seed = 0; -#endif - if (!seed || seed == M) -#ifdef UNIX - seed = ((long) getpid() << 16) ^ time(NULL); -#else - seed = ((long) clock() << 16) ^ time(NULL); -#endif - hi = seed / Q; - lo = seed % Q; - if ((seed = A * lo - R * hi) <= 0) - seed += M; - return seed; + fclose(f); + burn(buf); + return 0; } +/* Create a new state from the output of trueRandByte */ +void +cryptRandInit(struct IdeaCfbContext *cfb) +{ + byte buf[24]; + int i; -#ifndef PSEUDORANDOM /* use truly random numbers */ - -/* #define USEPCTIMER */ /* use fast hardware timer on IBM PC or AT or clone */ -/* #define DEBUG */ - -/* Prototypes for kbhit() (whether the keyboard has been hit) and getch() - (like getchar() but no echo, no buffering). Not available under some - implementations */ -#ifdef MSDOS -#include -typedef word16 fastcnt_t; -#else -typedef word32 fastcnt_t; -#ifdef VMS -int putch(int); -#else -#define putch(c) putc(c, stderr) -#endif -#endif /* !MSDOS */ - -#ifdef DEBUG -#define DEBUGprintf1(x) fprintf(stderr,x) -#define DEBUGprintf2(x,y) fprintf(stderr,x,y) -#else -#define DEBUGprintf1(x) -#define DEBUGprintf2(x,y) -#endif - -#define POOLSIZE 256 -static int randcount = 0; /* # of random bytes accumulated in pool */ -static byte randpool[POOLSIZE] = {0} ; /* pool of truly random bytes */ -static int recyclecount = 0 ; /* # of recycled random bytes accumulated */ -static byte recyclepool[POOLSIZE] = {0} ; /* pool of recycled random bytes */ - -static int accumpending = 0; /* # of random bits to add to next request */ - -static void randstir(byte *pool); - -/* fastcounter is a free-running counter incremented in main event loop. */ -static fastcnt_t fastcounter = 0; /* not needed if we can use the PC timer. */ -static fastcnt_t lastcounter = 0; -static int cbits; -static char toofast = 0; -/* toofast indicates keystroke rejected because it was typed too fast */ - -#ifdef AMIGA -int aecho; -#endif - -/* Information needed by the MD5 code for distilling large quantities of - semi-random information into a small amount of highly-random information. - This works as follows: - - Initially we have no random information. Whenever we add more slightly - random data, we put it in the MD5 data field and run MD5 over the - random byte pool. This propagates the randomness in the data evenly - throughout the pool due to the avalanche effect of the MD5 transformation. - The randomness transformation is carried out inside capturecounter() when - the data is entered, and is transparent to the operation of the rest of - the code. -*/ - -#include "md5.h" -static byte seedBuffer[ 64 ]; /* Buffer for MD5 seed value */ -static boolean isInitialised = FALSE; /* Whether buffer has been inited */ -static byte iv[ 16 ]; /* IV for MD5 transformation */ - -#ifdef USEPCTIMER /* we will use fast hardware timer on IBM PC */ -/* #include */ /* function definitions for inp() and outp() */ -/* outp() and inp() works only for Microsoft C for IBM PC or AT */ -/* timer0 on 8253-5 on IBM PC or AT tics every .84 usec. */ -#define timer0 0x40 /* 8253 timer 0 port */ -#define timercntl 0x43 /* 8253 control register */ -#define timer0rwl 0x00 /* read lo/hi bytes of cntr 2 with latch */ -#define timer0rnl 0x30 /* read lo/hi bytes of cntr 2 w/o latch */ - -static byte latched_hitimer = 0; /* captured by keyboard ISR */ -static byte latched_lotimer = 0; /* captured by keyboard ISR */ -/* when kbisr captures timer, timer_latched is set. */ -static boolean timer_latched = FALSE; - -static void kbisr(void) /* Keyboard Interrupt Service Routine (ISR) */ -/* - kbisr should be called on the way into, or on the way out of, - or from within the DOS keyboard ISR, as long as it gets called - at the time of a keyboard interrupt. Assumes that the real - DOS keyboard ISR captures the keystroke in the normal way. - Only the hardware timer counter is captured by the kbisr routine, - leaving the actual keystroke capture to the normal DOS keyboard ISR. - We indicate that a timer capture has taken place by setting - timer_latched. - - NOTE: WE STILL NEED TO FIND A WAY to connect this subroutine with the - normal keyboard ISR, so that kbisr gets called when there's a keyboard - interrupt. -*/ -{ outp(timercntl,timer0rwl); - latched_lotimer = inp(timer0); - latched_hitimer = inp(timer0); - timer_latched = TRUE; -} /* kbisr */ - -static unsigned short pctimer0(void) -{ -/* Reads and returns the hardware 8253 timer0 on the PC or AT -** or clone, shifted right 1 bit. -** -** DO NOT SET THE HARDWARE COUNTER TO ZERO. It is already initialized -** by the system to be used by the clock. It is set up in mode 3 -** (square wave rate generator) and counts down by 2 from 0 (0xFFFF+1) -** to produce an 18.2 Hz square wave. We may, however, READ the -** lo and hi bytes without causing any problems. BUT just -** remember that the lo byte will always be even (since it is -** counting by two). -** -** Note that we can not use counter 1 since it is tied to the -** dynamic RAM refresh hardware. Counter 2 is tied to the 8255 -** PPI chip to do things like sound. Though it would be safe to -** use counter 2 it is not desirable since we would have to turn -** the speaker on in order to make the timer count! Normally one -** sets counter 2 to mode 3 (square wave generator) to sound the -** speaker. You can set mode 2 (pulse generator) and the speaker -** hardly makes any sound at all, a click when you turn it on and -** a click when you turn it off. Counter 0 should be safe if -** we only read the counter bytes. -** -** WARNING: To use the hardware timer the way it really should be -** used, we ought to capture it via a keyboard interrupt service -** routine (ISR). Otherwise, we may experience weaknesses in randomness -** due to harmonic relationships between the hardware counter frequency -** and the keyboard software polling frequency. Unfortunately, this -** implementation does not currently use keyboard interrupts to -** capture the counter. This is not a problem if we don't use the -** hardware counter, but instead use the software counter fastcounter. -** Thus, the hardware counter should not be used at all, unless we -** support it with an ISR. -*/ - unsigned short t ; - /* See if timer has been latched by kbisr(). */ - if (!timer_latched) /* The timer was not already latched. */ - kbisr(); /* latch timer */ - /* return latched timer and clear latch */ - t = ( (((unsigned short) latched_hitimer) << 8) | - ((unsigned short) latched_lotimer) - ) >> 1 ; - timer_latched = FALSE; - return (t) ; -} /* pctimer0 */ - -#endif /* ifdef USEPCTIMER */ + for (i = 0; i < sizeof(buf); i++) + buf[i] = trueRandByte(); + if (cfb) + ideaCfbEncrypt(cfb, buf, buf, sizeof(buf)); + + ideaRandInit(&randContext, buf, buf+16); + randSeedOpen = TRUE; + burn(buf); +} +byte +cryptRandByte(void) +{ + if (!randSeedOpen) + cryptRandOpen((struct IdeaCfbContext *)0); + return ideaRandByte(&randContext); +} -/* keybuf is used only by keypress(), getkey(), and capturecounter(). */ -static short keybuf = 0; +/* + * Write out a file of random bytes. If cfb is defined, wash it with the + * cipher. + */ +int +cryptRandWriteFile(char const *name, struct IdeaCfbContext *cfb, unsigned bytes) +{ + byte buf[256]; + FILE *f; + int i, len; + + f = fopen(name, FOPWBIN); + if (!f) + return -1; + + while (bytes) { + len = (bytes < sizeof(buf)) ? bytes : sizeof(buf); + for (i = 0; i < len; i++) + buf[i] = ideaRandByte(&randContext); + if (cfb) + ideaCfbEncrypt(cfb, buf, buf, len); + i = fwrite(buf, 1, len, f); + if (i < len) + break; + bytes -= len; + } + return (fclose(f) != 0 || bytes != 0) ? -1 : 0; +} -#ifdef VMS +/* + * Create a new RNG state, encrypt it with the supplied key, and save it + * out to disk. + * + * When we encrypt a file, the saved data is "postwashed" using the + * same key and initial vector (including the repeated check bytes and + * everything) that is used to encrypt the user's message. + * The hope is that this "postwash" renders it is at least as hard to + * derive old session keys from randseed.bin as it is to crack the the + * message directly. + * + * The purpose of using EXACTLY the same encryption is to make sure that + * there isn't related, but different data floating around that can be + * used for cryptanalysis. + * + * This function is always called by exitPGP, without a washing encryption, + * so this function ignores that call if it has previously been called + * to save washed bytes. + */ +void +cryptRandSave(struct IdeaCfbContext *cfb) +{ + static boolean savedwashed = FALSE; -extern unsigned long vms_clock_bits[2]; /* VMS Hardware Clock */ -extern const long vms_ticks_per_update; /* Clock update int. */ + if (!randSeedOpen) + return; /* Do nothing */ -#endif /* VMS */ + if (cfb) + savedwashed = TRUE; + else if (savedwashed) + return; /* Don't re-save if it's already been saved washed. */ -static void capturecounter(void) -/* Push a fast counter on the random stack. Should be called when -** the user touches a key or clicks the mouse. -*/ -{ - fastcnt_t dt; -#ifndef MSDOS - byte *random_xtra; /* extra timer info */ -#endif - static byte abits = 0; /* number of accumulated bits in accum */ - unsigned int accum1; + (void)cryptRandWriteFile(globalRandseedName, cfb, RANDSEED_BYTES); +} -#define cbitsmask ((1 << cbits)-1) +/* + * True random bit handling + */ - /* fastcounter only contains timing information, in the form of a - free-running timer, either hardware or software. - accum1 contains stuff from fastcounter and other sources, - like the actual key the user hit. - */ - -#if defined(USEPCTIMER) /* we will use fast hardware timer on IBM PC */ - fastcounter = pctimer0(); /* capture hardware timer */ -#endif /* not USEPCTIMER */ +/* + * Because these truly random bytes are so unwieldy to accumulate, + * they can be regarded as a precious resource. Unfortunately, + * cryptographic key generation algorithms may require a great many + * random bytes while searching about for large random prime numbers. + * Fortunately, they need not all be truly random. We only need as + * many truly random bits as there are bits in the large prime we + * are searching for. These random bytes can be recycled and modified + * via pseudorandom numbers until the key is generated, without losing + * any of the integrity of randomness of the final key. + * + * The technique used is a pool of random numbers, which bytes are + * taken from successively and, when the end is reached, the pool + * is stirred using an irreversible hash function. Some (64 bytes) + * of the pool is not returned to ensure the sequence is not predictible + * from the values retriefed from trueRandByte(). To be precise, + * MD5Transform is used as a block cipher in CBC mode, and then the + * "key" (i.e. what is usually the material to be hashed) is overwritten + * with some of the just-generated random bytes. + * + * This is implemented in randpool.c; see that file for details. + * + * 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 (such as prime generation) or another. + * + * trueRandFlush is called to obliterate traces of old random bits after + * prime generation is completed. (Primes are the most carefully-guarded + * values in PGP.) + */ -#ifdef VMS -#define HW_TIMER 1 /* using hardware timer */ - /* Capture fast system timer: */ - SYS$GETTIM(vms_clock_bits); -#ifdef TEST_COUNTER - fprintf(stderr," time %10ol %10ol\n",vms_clock_bits[0],vms_clock_bits[1]); -#endif - /* vms_clock_bits[0] and vms_ticks_per_update are 32-bits each. */ - fastcounter = vms_clock_bits[0] / vms_ticks_per_update; -#endif /* VMS */ +static unsigned trueRandBits = 0; /* Bits of entropy in pool */ -#ifdef UNIX -#define HW_TIMER 1 /* using hardware timer */ -#ifdef ITIMER_REAL - { static struct itimerval it; - getitimer(ITIMER_REAL, &it); - if (it.it_value.tv_sec == 0 && it.it_value.tv_usec == 0) - { /* start the timer */ - it.it_value.tv_sec = 100000; - it.it_value.tv_usec = 0; - it.it_interval.tv_sec = 100000; - it.it_interval.tv_usec = 0; - signal(SIGALRM, SIG_IGN); /* just in case.. */ - setitimer(ITIMER_REAL, &it, NULL); - return; - } - /* divide by 64 (we can at least expect a 60Hz clock) */ - fastcounter = 6400000-(it.it_value.tv_usec / (1000000/64) - + it.it_value.tv_sec * 64); - random_xtra = (byte *) &it.it_value; -#define XTRA_BYTES (sizeof(struct timeval)) - } -#else /* !ITIMER_REAL */ - { static struct tms tms; - fastcounter = times(&tms); - random_xtra = (byte *) &tms; -#define XTRA_BYTES (sizeof(struct tms)) - } -#endif /* !ITIMER_REAL */ -#endif /* UNIX */ +/* trueRandPending is bits to add to next accumulation request */ +static unsigned trueRandPending = 0; - dt = fastcounter - lastcounter; -#ifdef TEST_COUNTER - fprintf(stderr,"%6lu", dt); -#endif -#ifndef NO_CBIT_STRIP - dt /= 6; /* use 2.5 bits less than the actual count */ -#endif - for (cbits = 0; dt; ++cbits) - dt >>= 1; -#if 0 /* I don't think this is necessary - CP */ - if (cbits > 8) - cbits = 8; -#endif - -#ifdef TEST_COUNTER - fprintf(stderr,"%6u\n", cbits); -#endif - if (cbits <= 0) - { - toofast++; /* indicate a too-fast keystroke */ - return; /* only captures if enough time has passed */ - } - - lastcounter = fastcounter; /* latch timer info */ +/* + * Destroys already-used random numbers. Ensures no sensitive data + * remains in memory that can be recovered later. This is called + * after RSA key generation, so speed is not critical, but security is. + * RSA key generation takes long enough that interrupts and other + * tasks are likely to have used a measurable and difficult-to-predict + * amount of real time, so there is some virtue in sampling the clocks + * with noise(). + */ +void +trueRandFlush(void) +{ + noise(); + randPoolStir(); /* Destroy evidence of what primes were generated */ + randPoolStir(); + randPoolStir(); + randPoolStir(); /* Make *really* certain */ +} - /* Initialise the MD5 info if necessary */ - if( !isInitialised ) /* we probably shouldn't bother */ - { memset(seedBuffer, 0, 64); - memset(iv, 0, 16); - isInitialised = TRUE; - } +/* + * "Consumes" count bits worth of entropy from the true random pool for some + * purpose, such as prime generation. + * + * Note that something like prime generation can end up calling trueRandByte + * more often than is implied by the count passed to trueRandClaim; this + * may happen if the random bit consumer is not perfectly efficient in its + * use of random bits. For example, if a search for a suitable prime fails, + * the easiest thing to do is to get another load of random bits and try + * again. It is perfectly acceptable if these bits are correlated with the + * bits used in the failed attempt, since they are discarded. + */ +void +trueRandConsume(unsigned count) +{ + assert (trueRandBits >= count); + trueRandBits -= count; +} - /* Add the slightly-random data to the MD5 input buffer. Currently we - just add a few bytes of environmental noise, but we could mix in - up to 512 bits worth. This should be extended in a system-specific - manner. - */ - seedBuffer[ 0 ] = keybuf; /* actual character user typed */ - seedBuffer[ 1 ] = lastcounter & 0xFF; - seedBuffer[ 2 ] = lastcounter >> 8; -#ifdef MSDOS - accum1 = ( int ) clock(); /* clock ticks */ - seedBuffer[ 3 ] = accum1 & 0xFF; - seedBuffer[ 4 ] = accum1 >> 8; -#endif - accum1 = ( int ) time(NULL); /* seconds */ - seedBuffer[ 5 ] = accum1 & 0xFF; - seedBuffer[ 6 ] = accum1 >> 8; -#ifdef XTRA_BYTES /* add some extra "random" stuff */ - memcpy(seedBuffer+16, random_xtra, XTRA_BYTES); -#endif - /* The preceding "noise quantum" has only cbits worth of randomness. */ - /* Spread it uniformly across the entire pool */ +/* + * Returns a truly random byte if any are available. It degenerates to + * a pseudorandom value if there are none. It is not an error to call + * this if none are available. For example, it is called when generating + * session keys to add to other sources of cryptographic random numbers. + * + * This forces an accumulation if any extra random bytes are pending. + */ +int +trueRandByte(void) +{ + if (trueRandPending) + trueRandAccum(0); - randstir(randpool); + return randPoolGetByte(); +} - /* - Now the somewhat dubious bit: In order to make this fit in with - the existing code, we use the existing mechanism of keeping track - of a high-water mark in the buffer. This is actually reasonably - realistic, since it keeps a count of what percentage of the full - buffer is random enough-- measuring the "molarity" of solution. - */ - - abits += cbits; /* Update count of bits of randomness */ - while (abits >= 8) - { /* We've got another byte's worth, increment the buffer pointer */ - if (randcount < sizeof(randpool)) - randcount++; /* not a byte counter-- now a molarity of randomness */ - abits -= 8; +/* + * 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 + * suspiscous and the randomness is counted as 0. + */ +unsigned +trueRandEvent(int event) +{ + static int event1 = 0, event2 = 0; + word32 delta; + unsigned cbits; + + delta = noise(); + randPoolAddBytes((byte *)&event, sizeof(event)); + + if (event == event1 && event == event2) { + cbits = 0; + } else { + event2 = event1; + event1 = event; + + for (cbits = 0; delta; cbits++) + delta >>= 1; + + /* Excessive paranoia? */ + if (cbits > 8) + cbits = 8; } -} /* capturecounter */ + trueRandBits += cbits; + if (trueRandBits > RANDPOOLBITS) + trueRandBits = RANDPOOLBITS; -/* Because these truly random bytes are so unwieldy to accumulate, - they can be regarded as a precious resource. Unfortunately, - cryptographic key generation algorithms may require a great many - random bytes while searching about for large random prime numbers. - Fortunately, they need not all be truly random. We only need as - many truly random bytes as there are bytes in the large prime we - are searching for. These random bytes can be recycled and modified - via pseudorandom numbers until the key is generated, without losing - any of the integrity of randomness of the final key. -*/ - - -static void randstir(byte *pool) -/* Stir up the recycled random number bin, via a pseudorandom generator - Any truly random bytes stored into the seedBuffer are hashed into the - pool, and then destroyed for security reasons. */ -{ int i; - int j; - /* Now use MD5 to diffuse the randomness in these bits over the random - byte pool, raising the salinity of randomness in the pool. The - transformation is carried out by "encrypting" the data in CFB mode - with MD5 as the block cipher */ - for(i = 0; i < POOLSIZE; i += 16) - { - Transform((unsigned long *) iv, (unsigned long *) seedBuffer); - for(j = 0; j < 16; j++) - pool[i+j] ^= iv[j]; - memcpy(iv, pool+i, 16); - } -/* CFB encryption is reversible; if we want the stirring operation to - be strictly one-way, we must destroy the key. The operation - accomplishes that, and increases the end-to-beginning feedback - in randstir(), in an attempt to make all bytes depend on all - other bytes. */ - memcpy(seedBuffer, pool+POOLSIZE-sizeof(seedBuffer), - sizeof(seedBuffer)); -} /* randstir */ - - -short randload(short bitcount) -/* Flushes stale recycled random bits and copies a fresh supply of raw - random bits from randpool to recyclepool. Returns actual number of - bits transferred. */ -{ int bytecount; - bytecount = (bitcount+7)/8; - bytecount = min(bytecount,randcount); - randflush(); /* reset recyclecount, discarding recyclepool */ - while (bytecount--) - recyclepool[recyclecount++] = randpool[--randcount]; - DEBUGprintf2("\nAllocating %d recycleable random bytes. ",recyclecount); - randstir(recyclepool); - recyclecount = sizeof(recyclepool); - return(recyclecount*8); -} /* randload */ - - -void randflush(void) /* destroys pool of recycled random numbers */ -/* Ensures no sensitive data remains in memory that can be recovered later. */ -{ -/* We do not actually destroy the random bytes (which would be somewhat - wasteful); we just stir them so that they are irretrievable. This - assumes that MD5 is truly one-way. */ - randstir(recyclepool); - recyclecount = 0; -} /* randflush */ - -short try_randombyte(void) -/* Returns a truly random byte if any are available, or -1 if none. - * it is not an error to call this if none are available. For example, - * make_random_ideakey calls it to add to its other sources of pseudo-random - * noise. - */ -{ - /* First try to get a cheap recycled random byte, if there are any. */ - if (recyclecount) /* nonempty recycled pool */ - { - byte b; - b = recyclepool[--recyclecount]; - if (!recyclecount) /* Used them all up? */ - randstir(recyclepool); /* Refresh recycled pool */ - return b; - } - - /* Empty recycled pool. Try a more expensive fresh random byte. */ + return cbits; +} - if (randcount) /* nonempty random pool--return a very random number */ - return (randpool[--randcount]); - /* No, all is for naught. Return -1. */ - return -1; +/* + * Since getting random bits from the keyboard requires user attention, + * we buffer up requests for them until we can do one big request. + */ +void +trueRandAccumLater(unsigned bitcount) +{ + trueRandPending += bitcount; /* Wow, that was easy! :-) */ } -short randombyte(void) -/* Returns truly random byte from pool, or a pseudorandom value -** if pool is empty. It is recommended that the caller check -** the value of randcount before calling randombyte. -*/ -{ - short r; - - r = try_randombyte(); - if (r >= 0) - return r; - - /* Oops! Random pool empty. Were there some unsatisifed deferred - accumulation requests? */ - randaccum(0); - r = try_randombyte(); - if (r >= 0) - return r; - - /* Alas, fresh random pool is truly empty. Complain, and return - a pseudorandom byte. Pseudorandom numbers are bad for - cryptographic applications. Although we will return a - pseudorandom byte in the low order byte, indicate error by - making the result negative in the high byte. - */ - fprintf(stderr, " \007WARNING: Random pool empty! "); - return ( (pseudorand() & 0xFF) ^ (-1) ); -} /* randombyte */ - - -static boolean keypress(void) /* TRUE iff keyboard input ready */ -{ /* Accumulates random numbers by timing user keystrokes. */ - static short lastkey = 0; /* used to detect autorepeat key sequences */ - static short next_to_lastkey = 0; /* allows a single repeated key */ - -#ifndef HW_TIMER /* no fast hardware timer available */ - fastcounter++; /* used in lieu of fast hardware timer counter */ -#endif /* ifndef HW_TIMER */ - - if (keybuf & 0x100) /* bit 8 means keybuf contains valid data */ - return( TRUE ); /* key was hit the last time thru */ - - /* - * If HW_TIMER is defined we don't need a busy loop, so keypress - * waits for a key and will always return TRUE. - */ -#ifndef HW_TIMER - if (kbhit() == 0) /* keyboard was not hit */ - return( FALSE ); +static void flush_input(void); + +/* + * Performs an accumulation of random bits. As long as there are fewer bits + * in the buffer than are needed (the number passed, plus pending bits), + * prompt for more. + */ +void +trueRandAccum(unsigned count) /* Get this many random bits ready */ +{ + int c; +#if defined(MSDOS) || defined(__MSDOS__) + time_t timer; #endif - keybuf = getch() | 0x100; /* set data latch bit */ + count += trueRandPending; /* Do deferred accumulation now */ + trueRandPending = 0; - /* Keyboard was hit. Decide whether to call capturecounter... */ + if (count > RANDPOOLBITS) + count = RANDPOOLBITS; - /* Guard against typahead buffer defeating fastcounter's randomness. - ** This could be a problem for multicharacter sequences generated - ** by a function key expansion or by the user generating keystrokes - ** faster than our event loop can handle them. Only the last - ** character of a multicharacter sequence will trigger the counter - ** capture. Also, don't let the keyboard's autorepeat feature - ** produce nonrandom counter capture. However, we do allow a - ** single repeated character to trigger counter capture, because - ** many english words have double letter combinations, and it's - ** unlikely a typist would exploit the autorepeat feature to - ** type a simple double letter sequence. - ** We have some separate checks in capturecounter() to guard - ** against other reasons for characters coming in too fast. - */ - -#ifndef HW_TIMER - if (kbhit() == 0) /* nothing in typahead buffer */ -#endif - { /* don't capture counter if key repeated */ - if (keybuf != lastkey) - capturecounter(); /* read random noise from environment */ - else if (keybuf != next_to_lastkey) /* allow single repeat */ - capturecounter(); - next_to_lastkey = lastkey; - lastkey = keybuf; - } - return( TRUE ); -} /* keypress */ + if (trueRandBits >= count) + return; + fprintf(stderr, +LANG("\nWe need to generate %u random bits. This is done by measuring the\ +\ntime intervals between your keystrokes. Please enter some random text\ +\non your keyboard until you hear the beep:\n"), count-trueRandBits); -static short getkey(void) /* Returns data from keyboard (no echo). */ -{ /* Also accumulates random numbers via keypress(). */ - while(! keypress() ) /* loop until key is pressed. */ - ; - return( keybuf &= 0xff); /* clear latch bit 8 */ -} /* getkey */ - - -#define BS 8 /* ASCII backspace */ -#define CR 13 /* ASCII carriage return */ -#define LF 10 /* ASCII linefeed */ -#define DEL 0177 /* ASCII delete */ - -/* Since getting random bits from the keyboard requires user attention, - we buffer up requests for them until we can do one big request. */ - -void randaccum_later(short bitcount) -{ - accumpending += bitcount; /* Wow, that was easy! :-) */ -} - -/* We will need a series of truly random bits for key generation. - In most implementations, our random number supply is derived from - random keyboard delays rather than a hardware random number - chip. So we will have to ensure we have a large enough pool of - accumulated random numbers from the keyboard. Later, randombyte - will return bytes one at a time from the accumulated pool of - random numbers. For ergonomic reasons, we may want to prefill - this random pool all at once initially. The randaccum function - prefills a pool of random bits. -*/ - -void randaccum(short bitcount) /* Get this many random bits ready */ -{ short nbytes; -#ifndef HW_TIMER - unsigned long timer; -#endif - int fasts_rejected = 0; /* number of too-fast keystrokes */ + ttycbreak(); - bitcount += accumpending; /* Do deferred accumulation now */ - accumpending = 0; - nbytes = min((bitcount+7)/8,sizeof(randpool)); - fasts_rejected = 0; /* number of too-fast keystrokes */ - toofast = 0; /* clear too-fast latch */ - - if (randcount < nbytes) /* if we don't have enough already */ - { fprintf(stderr, -PSTR("\nWe need to generate %d random bytes. This is done by measuring the\ -\ntime intervals between your keystrokes. Please enter some random text\ -\non your keyboard until you hear the beep:\n"), nbytes-randcount); -#ifdef NEEDBREAK - ttycbreak(); -#endif + do { /* display counter to show progress */ + fprintf(stderr,"\r%4d ", count-trueRandBits); + fflush(stderr); /* assure screen update */ - while (randcount < nbytes) - { -#ifndef TEST_COUNTER - fprintf(stderr,"\r%3d ",nbytes-randcount); + flush_input(); /* If there's no delay, we can't use it */ + c = getch(); /* always wait for input */ +#ifdef MSDOS + if (c == 3) + breakHandler(SIGINT); + if (c == 0) + c = getch() + 256; +#endif + /* Print flag indicating acceptance (or not) */ + putc(trueRandEvent(c) ? '.' : '?' , stderr); + } while (trueRandBits < count); + + fputs("\r 0 *", stderr); + fputs(LANG("\007 -Enough, thank you.\n"), stderr); + +#if defined(MSDOS) || defined(__MSDOS__) + /* Wait until one full second has passed without keyboard input */ + do { + flush_input(); + sleep(1); + } while (kbhit()); +#else + sleep(1); + flush_input(); #endif - fflush(stderr); - getkey(); - if (toofast) - { fasts_rejected++; /* keep score */ - toofast = 0; /* reset latch */ - putc('?', stderr); /* indicate trouble */ - } - else - putc('.', stderr); - } - fprintf(stderr,PSTR("\007*\n-Enough, thank you.\n")); + ttynorm(); +} - if (fasts_rejected > 2) /* Did user type too fast? */ - fprintf(stderr,PSTR("(Ignored %d keystrokes that were typed too fast.)\n"), - fasts_rejected); +#define BS 8 +#define LF 10 +#define CR 13 +#define DEL 127 -#ifdef HW_TIMER /* keypress() would block if using hardware timer */ - sleep(1); +#ifdef VMS +int putch(int); #else - /* Clean up typeahead for at least 1 full second... */ - timer = time(NULL) + 1; /* need at least 1 second of quiet */ - while ((unsigned long) time(NULL) <= timer) - { if (keypress()) /* user touched a key, reset timer */ - { getkey(); - timer = time(NULL) + 1; - } - } +#define putch(c) putc(c, stderr) #endif -#ifdef NEEDBREAK - ttynorm(); -#endif /* !NEEDBREAK */ - } -} /* randaccum */ - - -int getstring(char *strbuf, int maxlen, boolean echo) -/* Gets string from user, with no control characters allowed. - Also accumulates random numbers by calling getkey(). - maxlen is max length allowed for string. - echo is TRUE iff we should echo keyboard to screen. - Returns null-terminated string in strbuf. -*/ -{ short i; +int +getstring(char *strbuf, unsigned maxlen, int echo) +/* Gets string from user, with no control characters allowed. + * Also accumulates random numbers. + * maxlen is max length allowed for string. + * echo is TRUE iff we should echo keyboard to screen. + * Returns null-terminated string in strbuf. + */ +{ + unsigned i; char c; -#ifdef NEEDBREAK + ttycbreak(); -#endif + #ifdef AMIGA - aecho = (int)echo; - echo = FALSE; /* echo is done in getch */ + aecho = (int)echo; + echo = FALSE; /* echo is done in getch */ #endif /* AMIGA */ + fflush(stdout); i=0; - while (TRUE) - { + for (;;) { #ifndef VMS fflush(stderr); #endif /* VMS */ - c = getkey(); + c = getch(); + trueRandEvent(c); #ifdef VMS if (c == 25) { /* Control-Y detected */ ttynorm(); breakHandler(SIGINT); } #endif /* VMS */ - if (c==BS || c==DEL) - { if (i) - { if (echo) - { putch(BS); +#if defined(MSDOS) || defined (__MSDOS__) + if (c == 3) + breakHandler(SIGINT); +#endif + if (c==BS || c==DEL) { + if (i) { + if (echo) { + putch(BS); putch(' '); putch(BS); } i--; + } else { + putch('\007'); } continue; } - if (c < ' ' && c != LF && c != CR) - { putch('\007'); + if (c < ' ' && c != LF && c != CR) { + putch('\007'); +#if defined(MSDOS) || defined (__MSDOS__) + if (c == 3) + breakHandler(SIGINT); + if (c == 0) + getch(); /* Skip extended key codes */ +#endif continue; } - if (echo) putch(c); - if (c==CR) - { if (echo) putch(LF); + if (echo) + putch(c); + if (c==CR) { + if (echo) + putch(LF); break; } if (c==LF) @@ -741,28 +552,28 @@ int getstring(char *strbuf, int maxlen, if (c=='\n') break; strbuf[i++] = c; - if (i>=maxlen) - { fprintf(stderr,"\007*\n"); /* -Enough! */ - while (keypress()) - getkey(); /* clean up any typeahead */ + if (i >= maxlen) { + fputs("\007*\n", stderr); /* -Enough! */ +#if 0 + while (kbhit()) + getch(); /* clean up any typeahead */ +#endif break; } } strbuf[i] = '\0'; /* null termination of string */ -#ifdef NEEDBREAK + ttynorm(); -#endif - return(i); /* returns string length */ -} /* getstring */ + return(i); /* returns string length */ +} /* getstring */ -#endif /* ifndef PSEUDORANDOM */ -void flush_input(void) +static void +flush_input(void) { /* on unix ttycbreak() will flush the input queue */ -#ifndef HW_TIMER - while (keypress()) /* flush typahead buffer */ - getkey(); +#if defined(MSDOS) || defined (__MSDOS__) + while (kbhit()) /* flush typahead buffer */ + getch(); #endif } -