--- pgp/src/random.c 2018/04/24 16:40:51 1.1.1.6 +++ pgp/src/random.c 2018/04/24 16:45:18 1.1.1.9 @@ -1,7 +1,7 @@ /* * True and cryptographic random number generation. * - * (c) Copyright 1990-1994 by Philip Zimmermann. All rights reserved. + * (c) Copyright 1990-1996 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. @@ -28,6 +28,19 @@ #include /* For SIGINT */ #include +#ifdef AMIGA /* Includes for timer -- RKNOP */ +#include +#include +#include +#include +#include +#include +#endif /* AMIGA */ + +#ifdef __PUREC__ +#include +#endif + #include "system.h" /* For ttycbreak, getch, etc. */ #include "idea.h" #include "md5.h" @@ -37,90 +50,210 @@ #include "fileio.h" /* For FOPRBIN */ #include "pgp.h" /* For globalRandseedName */ #include "randpool.h" +#ifdef MACTC5 +#include "Macutil2.h" +#include "Macutil3.h" +#include "TimeManager.h" +#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. + */ + +/* + * 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 int randInitFlag = 0; /* * 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(void) +cryptRandOpen(struct IdeaCfbContext *cfb) { - byte buf[24]; + byte buf[256]; int len; FILE *f; - if (randInitFlag) - return 0; + 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); - fclose(f); + if (cfb) + ideaCfbEncrypt(cfb, buf, buf, 24); ideaRandInit(&randContext, buf, buf+16); - randInitFlag = 1; + randSeedOpen = TRUE; + if (len != 24) { /* Error */ + fclose(f); + return -1; + } + + /* 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); + } - return (len == 24) ? 0 : -1; + fclose(f); + burn(buf); +#ifdef MACTC5 + PGPSetFinfo(globalRandseedName,'RSed','MPGP'); +#endif + return 0; } +/* Create a new state from the output of trueRandByte */ void -cryptRandCreate(void) +cryptRandInit(struct IdeaCfbContext *cfb) { - byte randbuf[24]; + byte buf[24]; int i; - FILE *f; - for (i = 0; i < 24; i++) - randbuf[i] = trueRandByte(); + for (i = 0; i < sizeof(buf); i++) + buf[i] = trueRandByte(); + if (cfb) + ideaCfbEncrypt(cfb, buf, buf, sizeof(buf)); - ideaRandInit(&randContext, (byte *)randbuf, (byte *)randbuf+16); - randInitFlag = 1; + ideaRandInit(&randContext, buf, buf+16); + randSeedOpen = TRUE; + burn(buf); +} - f = fopen(globalRandseedName, FOPWBIN); - if (f) { - fwrite(randbuf, 1, 24, f); - fclose(f); - } - return; +byte +cryptRandByte(void) +{ + if (!randSeedOpen) + cryptRandOpen((struct IdeaCfbContext *)0); + return ideaRandByte(&randContext); } /* - * Encrypt the state of the PRNG with the given key. It is intended - * that this key is the md5 of the message to be encrypted, which is - * unpredictable to a would-be attacker who does not posess the message. - * This is simply a way to get some "random" bytes without a random - * number source. This "prewash" attempts to reduce the value of a - * captured randseed.bin file. + * Write out a file of random bytes. If cfb is defined, wash it with the + * cipher. */ -void -cryptRandWash(byte const key[16]) +int +cryptRandWriteFile(char const *name, struct IdeaCfbContext *cfb, unsigned bytes) { - struct IdeaCfbContext cfb; + byte buf[256]; + FILE *f; + int i, len; - if (!randInitFlag) - cryptRandOpen(); - ideaCfbInit(&cfb, key); - ideaRandWash(&randContext, &cfb); - ideaCfbDestroy(&cfb); -} + f = fopen(name, FOPWBIN); + if (!f) + return -1; -byte -cryptRandByte(void) -{ - if (!randInitFlag) - cryptRandOpen(); - return ideaRandByte(&randContext); + 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; + } + +#ifdef MACTC5 + PGPSetFinfo((char *)name,'RSed','MPGP'); +#endif + + return (fclose(f) != 0 || bytes != 0) ? -1 : 0; } /* - * Create a new RNG state, encrypt it with the session key, and save it - * out to disk. The RNG is re-initialized with the saved parameters. + * Create a new RNG state, encrypt it with the supplied key, and save it + * out to disk. * - * This uses the same key and initial vector (including the repeated - * check bytes and everything) that is used to encrypt the user's message. + * 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. @@ -128,34 +261,34 @@ cryptRandByte(void) * 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. */ +#ifdef MACTC5 + boolean savedwashed = FALSE; +#endif + void -cryptRandSave(byte const key[16], byte const iv[8]) +cryptRandSave(struct IdeaCfbContext *cfb) { - struct IdeaCfbContext cfb; - byte buf[24]; - FILE *f; - int i; +#ifndef MACTC5 + static boolean savedwashed = FALSE; +#endif - /* This IV is EXACTLY the same as is used on bulk files. */ - memcpy(buf, iv, 8); - buf[8] = buf[6]; /* "check bytes" */ - buf[9] = buf[7]; - - ideaCfbInit(&cfb, key); - ideaCfbEncrypt(&cfb, buf, buf, 10); - ideaCfbSync(&cfb); - for (i = 0; i < 24; i++) - buf[i] = ideaRandByte(&randContext); - ideaCfbEncrypt(&cfb, buf, buf, 24); - ideaCfbDestroy(&cfb); - ideaRandInit(&randContext, buf, buf+16); + if (!randSeedOpen) + return; /* Do nothing */ - f = fopen(globalRandseedName, FOPWBIN); - if (f) { - fwrite(buf, 1, 24, f); - fclose(f); - } + if (cfb) + savedwashed = TRUE; + else if (savedwashed) + return; /* Don't re-save if it's already been saved washed. */ + + (void)cryptRandWriteFile(globalRandseedName, cfb, RANDSEED_BYTES); +#ifdef MACTC5 + PGPSetFinfo(globalRandseedName,'RSed','MPGP'); +#endif } /* @@ -196,6 +329,23 @@ cryptRandSave(byte const key[16], byte c static unsigned trueRandBits = 0; /* Bits of entropy in pool */ +#ifdef MACTC5 +#define CBITS 5 +#define TRByt 3 +#define TREvt 1 + +static void perturb(rbits) +int rbits; +{ + spinner(); + randPoolAddBytes((byte *) seedBuffer, 8); + trueRandBits +=rbits; + if (trueRandBits > RANDPOOLBITS) + trueRandBits = RANDPOOLBITS; + return; +} +#endif + /* trueRandPending is bits to add to next accumulation request */ static unsigned trueRandPending = 0; @@ -233,8 +383,15 @@ trueRandFlush(void) void trueRandConsume(unsigned count) { +#ifdef MACTC5 + if( trueRandBits >= count ) + trueRandBits -= count; + else + trueRandBits = 0; +#else assert (trueRandBits >= count); trueRandBits -= count; +#endif } /* @@ -250,6 +407,12 @@ trueRandByte(void) { if (trueRandPending) trueRandAccum(0); +#ifdef MACTC5 + while( trueRandBits < 8 ) { + perturb(TRByt); + spinner(); + } +#endif return randPoolGetByte(); } @@ -274,6 +437,10 @@ trueRandEvent(int event) delta = noise(); randPoolAddBytes((byte *)&event, sizeof(event)); +#ifdef MACTC5 + perturb(TRByt); +#endif + if (event == event1 && event == event2) { cbits = 0; } else { @@ -284,8 +451,13 @@ trueRandEvent(int event) delta >>= 1; /* Excessive paranoia? */ +#ifdef MACTC5 + if (cbits > CBITS) + cbits = CBITS; +#else if (cbits > 8) cbits = 8; +#endif } trueRandBits += cbits; @@ -304,10 +476,22 @@ void trueRandAccumLater(unsigned bitcount) { trueRandPending += bitcount; /* Wow, that was easy! :-) */ +#ifdef MACTC5 + spinner(); +#endif } static void flush_input(void); +#ifdef AMIGA /* Globals used for timing here and in noise.c - RKNOP 940613 */ +struct Library *TimerBase=NULL; +struct timerequest *TimerIO=NULL; +union { struct timeval t; + struct EClockVal e; + } time0,time1; +unsigned short use_eclock=0; +#endif /* AMIGA */ + /* * 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), @@ -337,12 +521,38 @@ LANG("\nWe need to generate %u random bi ttycbreak(); +#ifdef AMIGA /* RKNOP 940613 */ + TimerIO=(struct timerequest *)AllocMem(sizeof(struct timerequest), + MEMF_PUBLIC|MEMF_CLEAR); + if (TimerIO) + { if (OpenDevice(TIMERNAME,UNIT_MICROHZ,(struct IORequest *)TimerIO,0)) + TimerBase=NULL; + else + { TimerBase=(struct Library *)TimerIO->tr_node.io_Device; + if (TimerBase->lib_Version>=36) /* Use E-clock instead */ + { use_eclock=1; + CloseDevice((struct IORequest *)TimerIO); + if (!OpenDevice(TIMERNAME,UNIT_ECLOCK, + (struct IORequest *)TimerIO,0)) + TimerBase=(struct Library *)TimerIO->tr_node.io_Device; + else + TimerBase=NULL; + } + else use_eclock=0; + } + } + else TimerBase=NULL; +#endif /* AMIGA */ + do { /* display counter to show progress */ fprintf(stderr,"\r%4d ", count-trueRandBits); fflush(stderr); /* assure screen update */ flush_input(); /* If there's no delay, we can't use it */ +#ifdef MACTC5 + StartTMCounter(); +#endif c = getch(); /* always wait for input */ #ifdef MSDOS if (c == 3) @@ -351,7 +561,11 @@ LANG("\nWe need to generate %u random bi c = getch() + 256; #endif /* Print flag indicating acceptance (or not) */ - putc(trueRandEvent(c) ? '.' : '?' , stderr); + /* putc a macro, not safe to have function as an arg!! */ + fputc(trueRandEvent(c) ? '.' : '?' , stderr); +#ifdef MACTC5 + StopTMCounter(); +#endif } while (trueRandBits < count); fputs("\r 0 *", stderr); @@ -364,17 +578,27 @@ LANG("\nWe need to generate %u random bi sleep(1); } while (kbhit()); #else +#ifdef AMIGA /* Added RKNOP 940608 */ + Delay(50*1); /* dos.library function, wait 1 second */ + if (TimerBase) CloseDevice((struct IORequest *)TimerIO); + TimerBase=NULL; + if (TimerIO) FreeMem(TimerIO,sizeof(struct timerequest)); + TimerIO=NULL; +#else sleep(1); flush_input(); #endif +#endif ttynorm(); } +#ifndef EBCDIC /* already defined in usuals.h */ #define BS 8 #define LF 10 #define CR 13 #define DEL 127 +#endif #ifdef VMS int putch(int); @@ -396,10 +620,11 @@ getstring(char *strbuf, unsigned maxlen, ttycbreak(); -#ifdef AMIGA - aecho = (int)echo; - echo = FALSE; /* echo is done in getch */ -#endif /* AMIGA */ +#ifdef AMIGA /* In case of -f (use stdio for plaintext input), + use ReqTools for input from the user */ + if (!IsInteractive(Input())) + return AmigaRequestString(strbuf,maxlen,echo); +#endif fflush(stdout); i=0; @@ -427,6 +652,8 @@ getstring(char *strbuf, unsigned maxlen, putch(BS); } i--; + } else { + putch('\007'); } continue; } @@ -472,7 +699,7 @@ getstring(char *strbuf, unsigned maxlen, static void flush_input(void) { /* on unix ttycbreak() will flush the input queue */ -#if defined(MSDOS) || defined (__MSDOS__) +#if defined(MSDOS) || defined (__MSDOS__) || defined(MACTC5) while (kbhit()) /* flush typahead buffer */ getch(); #endif