_ALGORITHM ALLEY COLUMN_ edited by Bruce Schneier "Truly Random Numbers" by Colin Plumb Listing One /* usuals.h -- Useful typedefs */ #ifndef USUALS_H #define USUALS_H #include #if UCHAR_MAX == 0xFF typedef unsigned char byte; /* 8-bit byte */ #else #error No 8-bit type found #endif #if UINT_MAX == 0xFFFFFFFF typedef unsigned int word32; /* 32-bit word */ #elif ULONG_MAX == 0xFFFFFFFF typedef unsigned long word32; #else #error No 32-bit type found #endif #endif /* USUALS_H */ Listing Two /* randtest.c -- Test application for the random-number routines */ #include #include #include /* For rand(), srand() and RAND_MAX */ #include "randpool.h" #include "noise.h" /* This function returns pseudo-random numbers uniformly from 0..range-1. */ static unsigned randRange(unsigned range) { unsigned result, div = ((unsigned)RAND_MAX+1)/range; while ((result = rand()/div) == range) /* retry */ ; return result; } /* Cute Wargames-like random effect thrown in for fun */ static void funnyprint(char const *string) { static const char alphabet[] = "ABCDEFGHIJKLMNOPWRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890"; char c, flag[80] = {0}; /* 80 is maximum line length */ unsigned i, tumbling = 0, len = strlen(string); /* We don't need good random numbers, so just use a good seed */ randPoolGetBytes((byte *)i, sizeof(i)); srand(i); /* Sometimes simple PRNGs are useful! */ /* Truncate longer strings (unless you have a better idea) */ if (len > sizeof(flag)) len = sizeof(flag); /* Count letters that we can tumble (letters in the alphabet) */ for (i = 0; i < len; i++) { if (strchr(alphabet, string[i])) { flag[i] = 1; /* Increase this for more tumbling */ tumbling++; } } /* Print until all characters are stable. */ do { putchar('\r'); for (i = 0; i < len; i++) { if (flag[i]) { c = alphabet[randRange(sizeof(alphabet)-1)]; if (c == string[i] && --flag[i] == 0) tumbling--; } else { c = string[i]; } putchar(c); } fflush(stdout); } while (tumbling); putchar('\n'); } #include /* For getch() */ #define FRACBITS 4 /* We count in 1/16 of a bit increments. */ /* Gather entropy from keyboard timing. This is currently MS-DOS specific. */ static void randAccum(int bits) { word32 delta; int c, oldc = 0, olderc = 0; if (bits > RANDPOOLBITS) bits = RANDPOOLBITS; bits <<= FRACBITS; puts("We are generating some truly random bits by timing your\n" "keystrokes. Please type until the counter reaches 0.\n"); while (bits > 0) { printf("\r%4d ", (bits-1 >> FRACBITS) + 1); c = getch(); delta = noise()/6; /* Add time of keystroke */ if (c == 0) c = 0x100 + getch(); /* Handle function keys */ randPoolAddBytes((byte const *)&c, sizeof(c)); /* Normal typing has double letters, but discard triples */ if (c == oldc && c == olderc) continue; olderc = oldc; oldc = c; if (delta) { /* Subtract log2(delta) from bits */ /* Integer bits first, normalizing */ bits -= 31<>= 1) { delta >>= 16; delta *= delta; if (delta >= 1ul<<31) bits -= c; else delta <<= 1; } } } puts("\r 0 Thank you, that's enough."); } /* When invoked with the argument "foo", this should start with: * Adding "foo\0" to pool. Pseudo-random bytes: * 4c 9d 41 ba 44 41 63 a1 db 1c ab 3f 52 a1 a2 84 c3 e5 dc bc 57 4c d9 f3 38 * d7 45 50 f9 94 36 96 a3 df 90 ff 23 e5 ec 3c 76 1f ce 1c bc d6 79 8b 5e e7 * aa 97 16 c0 50 c6 95 0b c1 62 42 e5 5b 8f d7 bd d7 70 1f c6 60 6a 5f f3 74 * 8d 35 ad 51 5a 4a 0c 02 cd d5 36 7e d4 c2 d9 f0 d3 49 ed 2d fa 4e 2b 70 3f */ int main(int argc, char **argv) { int i; while (--argc) { printf("Adding \"%s\\0\" to the pool.\n", *++argv); randPoolAddBytes((byte const *)*argv, strlen(*argv)+1); } puts("\nPseudo-random bytes:"); i = 100; while (i--) printf("%02x%c", randPoolGetByte(), i % 25 ? ' ' : '\n'); putchar('\n'); funnyprint("This will be deterministic on a given system."); putchar('\n'); noise(); /* Establish a baseline for the deltas */ randAccum(800); /* 800 random bits = 100 random bytes */ puts("\nTruly random bytes:"); i = 100; while (i--) printf("%02x%c", randPoolGetByte(), i % 25 ? ' ' : '\n'); putchar('\n'); funnyprint("This will be unpredictable."); return 0; } Listing Three /* md5.h -- declarations for md5.c */ #ifndef MD5_H #define MD5_H #include "usuals.h" struct MD5Context { word32 hash[4]; word32 bytes[2]; word32 input[16]; }; void byteSwap(word32 *buf, unsigned words); void MD5Init(struct MD5Context *context); void MD5Update(struct MD5Context *context, byte const *buf, unsigned len); void MD5Final(byte digest[16], struct MD5Context *context); void MD5Transform(word32 hash[4], word32 const input[16]); #endif /* !MD5_H */ Listing Four /* md5.c -- An implementation of Ron Rivest's MD5 message-digest algorithm. * Written by Colin Plumb in 1993, no copyright is claimed. This code is in the * public domain; do with it what you wish. Equivalent code is available from * RSA Data Security, Inc. This code does not oblige you to include legal * boilerplate in the documentation. To compute the message digest of a string * of bytes, declare an MD5Context structure, pass it to MD5Init, call * MD5Update as needed on buffers full of bytes, and then call MD5Final, which * will fill a supplied 16-byte array with the digest. */ #include /* for memcpy() */ #include "md5.h" /* Byte-swap an array of words to little-endian. (Byte-sex independent) */ void byteSwap(word32 *buf, unsigned words) { byte *p = (byte *)buf; do { *buf++ = (word32)((unsigned)p[3]<<8 | p[2]) << 16 | ((unsigned)p[1]<<8 | p[0]); p += 4; } while (--words); } /* Start MD5 accumulation. */ void MD5Init(struct MD5Context *ctx) { ctx->hash[0] = 0x67452301; ctx->hash[1] = 0xefcdab89; ctx->hash[2] = 0x98badcfe; ctx->hash[3] = 0x10325476; ctx->bytes[1] = ctx->bytes[0] = 0; } /* Update ctx to reflect the addition of another buffer full of bytes. */ void MD5Update(struct MD5Context *ctx, byte const *buf, unsigned len) { word32 t = ctx->bytes[0]; if ((ctx->bytes[0] = t + len) < t) /* Update 64-bit byte count */ ctx->bytes[1]++; /* Carry from low to high */ t = 64 - (t & 0x3f); /* Bytes available in ctx->input (>= 1) */ if (t > len) { memcpy((byte *)ctx->input+64-t, buf, len); return; } /* First chunk is an odd size */ memcpy((byte *)ctx->input+64-t, buf, t); byteSwap(ctx->input, 16); MD5Transform(ctx->hash, ctx->input); buf += t; len -= t; /* Process data in 64-byte chunks */ while (len >= 64) { memcpy(ctx->input, buf, 64); byteSwap(ctx->input, 16); MD5Transform(ctx->hash, ctx->input); buf += 64; len -= 64; } /* Buffer any remaining bytes of data */ memcpy(ctx->input, buf, len); } /* Final wrapup - pad to 64-byte boundary with the bit pattern * 1 0* (64-bit count of bits processed, LSB-first) */ void MD5Final(byte digest[16], struct MD5Context *ctx) { int count = ctx->bytes[0] & 0x3F; /* Bytes mod 64 */ byte *p = (byte *)ctx->input + count; /* Set the first byte of padding to 0x80. There is always room. */ *p++ = 0x80; /* Bytes of zero padding needed to make 56 bytes (-8..55) */ count = 56 - 1 - count; if (count < 0) { /* Padding forces an extra block */ memset(p, 0, count+8); byteSwap(ctx->input, 16); MD5Transform(ctx->hash, ctx->input); p = (byte *)ctx->input; count = 56; } memset(p, 0, count); byteSwap(ctx->input, 14); /* Append 8 bytes of length in *bits* and transform */ ctx->input[14] = ctx->bytes[0] << 3; ctx->input[15] = ctx->bytes[1] << 3 | ctx->bytes[0] >> 29; MD5Transform(ctx->hash, ctx->input); byteSwap(ctx->hash, 4); memcpy(digest, ctx->hash, 16); memset(ctx, 0, sizeof(*ctx)); /* In case it's sensitive */ } /* The four core functions */ #define F1(x, y, z) (z ^ (x & (y ^ z))) #define F2(x, y, z) F1(z, x, y) #define F3(x, y, z) (x ^ y ^ z) #define F4(x, y, z) (y ^ (x | ~z)) /* This is the central step in the MD5 algorithm. */ #define MD5STEP(f,w,x,y,z,in,s) (w += f(x,y,z)+in, w = (w<>32-s) + x) /* The heart of the MD5 algorithm. */ void MD5Transform(word32 hash[4], word32 const input[16]) { register word32 a = hash[0], b = hash[1], c = hash[2], d = hash[3]; MD5STEP(F1, a, b, c, d, input[ 0]+0xd76aa478, 7); MD5STEP(F1, d, a, b, c, input[ 1]+0xe8c7b756, 12); MD5STEP(F1, c, d, a, b, input[ 2]+0x242070db, 17); MD5STEP(F1, b, c, d, a, input[ 3]+0xc1bdceee, 22); MD5STEP(F1, a, b, c, d, input[ 4]+0xf57c0faf, 7); MD5STEP(F1, d, a, b, c, input[ 5]+0x4787c62a, 12); MD5STEP(F1, c, d, a, b, input[ 6]+0xa8304613, 17); MD5STEP(F1, b, c, d, a, input[ 7]+0xfd469501, 22); MD5STEP(F1, a, b, c, d, input[ 8]+0x698098d8, 7); MD5STEP(F1, d, a, b, c, input[ 9]+0x8b44f7af, 12); MD5STEP(F1, c, d, a, b, input[10]+0xffff5bb1, 17); MD5STEP(F1, b, c, d, a, input[11]+0x895cd7be, 22); MD5STEP(F1, a, b, c, d, input[12]+0x6b901122, 7); MD5STEP(F1, d, a, b, c, input[13]+0xfd987193, 12); MD5STEP(F1, c, d, a, b, input[14]+0xa679438e, 17); MD5STEP(F1, b, c, d, a, input[15]+0x49b40821, 22); MD5STEP(F2, a, b, c, d, input[ 1]+0xf61e2562, 5); MD5STEP(F2, d, a, b, c, input[ 6]+0xc040b340, 9); MD5STEP(F2, c, d, a, b, input[11]+0x265e5a51, 14); MD5STEP(F2, b, c, d, a, input[ 0]+0xe9b6c7aa, 20); MD5STEP(F2, a, b, c, d, input[ 5]+0xd62f105d, 5); MD5STEP(F2, d, a, b, c, input[10]+0x02441453, 9); MD5STEP(F2, c, d, a, b, input[15]+0xd8a1e681, 14); MD5STEP(F2, b, c, d, a, input[ 4]+0xe7d3fbc8, 20); MD5STEP(F2, a, b, c, d, input[ 9]+0x21e1cde6, 5); MD5STEP(F2, d, a, b, c, input[14]+0xc33707d6, 9); MD5STEP(F2, c, d, a, b, input[ 3]+0xf4d50d87, 14); MD5STEP(F2, b, c, d, a, input[ 8]+0x455a14ed, 20); MD5STEP(F2, a, b, c, d, input[13]+0xa9e3e905, 5); MD5STEP(F2, d, a, b, c, input[ 2]+0xfcefa3f8, 9); MD5STEP(F2, c, d, a, b, input[ 7]+0x676f02d9, 14); MD5STEP(F2, b, c, d, a, input[12]+0x8d2a4c8a, 20); MD5STEP(F3, a, b, c, d, input[ 5]+0xfffa3942, 4); MD5STEP(F3, d, a, b, c, input[ 8]+0x8771f681, 11); MD5STEP(F3, c, d, a, b, input[11]+0x6d9d6122, 16); MD5STEP(F3, b, c, d, a, input[14]+0xfde5380c, 23); MD5STEP(F3, a, b, c, d, input[ 1]+0xa4beea44, 4); MD5STEP(F3, d, a, b, c, input[ 4]+0x4bdecfa9, 11); MD5STEP(F3, c, d, a, b, input[ 7]+0xf6bb4b60, 16); MD5STEP(F3, b, c, d, a, input[10]+0xbebfbc70, 23); MD5STEP(F3, a, b, c, d, input[13]+0x289b7ec6, 4); MD5STEP(F3, d, a, b, c, input[ 0]+0xeaa127fa, 11); MD5STEP(F3, c, d, a, b, input[ 3]+0xd4ef3085, 16); MD5STEP(F3, b, c, d, a, input[ 6]+0x04881d05, 23); MD5STEP(F3, a, b, c, d, input[ 9]+0xd9d4d039, 4); MD5STEP(F3, d, a, b, c, input[12]+0xe6db99e5, 11); MD5STEP(F3, c, d, a, b, input[15]+0x1fa27cf8, 16); MD5STEP(F3, b, c, d, a, input[ 2]+0xc4ac5665, 23); MD5STEP(F4, a, b, c, d, input[ 0]+0xf4292244, 6); MD5STEP(F4, d, a, b, c, input[ 7]+0x432aff97, 10); MD5STEP(F4, c, d, a, b, input[14]+0xab9423a7, 15); MD5STEP(F4, b, c, d, a, input[ 5]+0xfc93a039, 21); MD5STEP(F4, a, b, c, d, input[12]+0x655b59c3, 6); MD5STEP(F4, d, a, b, c, input[ 3]+0x8f0ccc92, 10); MD5STEP(F4, c, d, a, b, input[10]+0xffeff47d, 15); MD5STEP(F4, b, c, d, a, input[ 1]+0x85845dd1, 21); MD5STEP(F4, a, b, c, d, input[ 8]+0x6fa87e4f, 6); MD5STEP(F4, d, a, b, c, input[15]+0xfe2ce6e0, 10); MD5STEP(F4, c, d, a, b, input[ 6]+0xa3014314, 15); MD5STEP(F4, b, c, d, a, input[13]+0x4e0811a1, 21); MD5STEP(F4, a, b, c, d, input[ 4]+0xf7537e82, 6); MD5STEP(F4, d, a, b, c, input[11]+0xbd3af235, 10); MD5STEP(F4, c, d, a, b, input[ 2]+0x2ad7d2bb, 15); MD5STEP(F4, b, c, d, a, input[ 9]+0xeb86d391, 21); hash[0] += a; hash[1] += b; hash[2] += c; hash[3] += d; } Listing Five /* noise.h -- get environmental noise for RNG */ #include "usuals.h" word32 noise(void); Listing Six /* noise.c -- Get environmental noise. * This is adapted from code in the Pretty Good Privacy (PGP) package. * Written by Colin Plumb. */ #include #include "usuals.h" #include "randpool.h" #include "noise.h" #if defined(MSDOS) || defined(__MSDOS__) /* Use 1.19 MHz PC timer */ #include /* for enable() and disable() */ #include /* for inp() and outp() */ /* This code gets as much information as possible out of 8253/8254 timer 0, * which ticks every .84 microseconds. There are three cases: * 1) Original 8253. 15 bits available, as the low bit is unused. * 2) 8254, in mode 3. The 16th bit is available from the status register. * 3) 8254, in mode 2. All 16 bits of the counters are available. * (This is not documented anywhere, but I've seen it!) * This code repeatedly tries to latch the status (ignored by an 8253) and * sees if it looks like xx1101x0. If not, it's definitely not an 8254. * Repeat this a few times to make sure it is an 8254. */ static int has8254(void) { int i, s1, s2; for (i = 0; i < 5; i++) { disable(); outp(0x43, 0xe2); /* Latch status for timer 0 */ s1 = inp(0x40); /* If 8253, read timer low byte */ outp(0x43, 0xe2); /* Latch status for timer 0 */ s2 = inp(0x40); /* If 8253, read timer high byte */ enable(); if ((s1 & 0x3d) != 0x34 || (s2 & 0x3d) != 0x34) return 0; /* Ignoring status latch; 8253 */ } return 1; /* Status reads as expected; 8254 */ } static unsigned read8254(void) { unsigned status, count; disable(); outp(0x43, 0xc2); /* Latch status and count for timer 0 */ status = inp(0x40); count = inp(0x40); count |= inp(0x40) << 8; enable(); /* The timer is usually in mode 3, but some BIOSes use mode 2. */ if (status & 2) count = count>>1 | (status & 0x80)<<8; return count; } static unsigned read8253(void) { unsigned count; disable(); outp(0x43, 0x00); /* Latch count for timer 0 */ count = (inp(0x40) & 0xff); count |= (inp(0x40) & 0xff) << 8; enable(); return count >> 1; } #endif /* MSDOS || __MSDOS__ */ #ifdef UNIX #include #include /* For gettimeofday() */ #include /* for times() */ #include /* For qsort() */ #define N 15 /* Number of deltas to try (at least 5, preferably odd) */ /* Function needed for qsort() */ static int noiseCompare(void const *p1, void const *p2) { return *(int const *)p1 - *(int const *)p2; } /* Find the resolution of the gettimeofday() clock */ static unsigned noiseTickSize(void) { int i = 0, j = 0, d[N]; struct timeval tv0, tv1, tv2; gettimeofday(&tv0, (struct timezone *)0); tv1 = tv0; do { gettimeofday(&tv2, (struct timezone *)0); if (tv2.tv_usec > tv1.tv_usec+2) { d[i++] = tv2.tv_usec - tv0.tv_usec + 1000000 * (tv2.tv_sec - tv0.tv_sec); tv0 = tv2; j = 0; } else if (++j > 10000) /* Always getting <= 2 us, */ return 2; /* so assume 2us ticks */ tv1 = tv2; } while (i < N); /* Return average of middle 5 values (rounding up) */ qsort(d, N, sizeof(d[0]), noiseCompare); return (d[N/2-2]+d[N/2-1]+d[N/2]+d[N/2+1]+d[N/2+2]+4)/5; } #endif /* UNIX */ /* Add as much time-dependent random noise to the randPool as possible. */ word32 noise(void) { static word32 lastcounter; word32 delta; time_t tnow; clock_t cnow; #if defined(MSDOS) || defined(__MSDOS__) static unsigned deltamask = 0; unsigned t; if (deltamask == 0) deltamask = has8254() ? 0xffff : 0x7fff; t = (deltamask & 0x8000) ? read8254() : read8253(); randPoolAddBytes((byte const *)&t, sizeof(t)); delta = deltamask & (t - (unsigned)lastcounter); lastcounter = t; #elif defined(VMS) word32 t[2]; SYS$GETTIM(t); /* VMS hardware clock increments by 100000 per tick */ randPoolAddBytes((byte const *)t, sizeof(t)); delta = (t[0]-lastcounter)/100000; lastcounter = t[0]; #elif defined(UNIX) static unsigned ticksize = 0; struct timeval tv; struct tms tms; gettimeofday(&tv, (struct timezone *)0); randPoolAddBytes((byte const *)&tv, sizeof(tv)); cnow = times(&tms); randPoolAddBytes((byte const *)&tms, sizeof(tms)); randPoolAddBytes((byte const *)&cnow, sizeof(cnow)); tv.tv_usec += tv.tv_sec * 1000000; /* Unsigned, so wrapping is okay */ if (!ticksize) ticksize = noiseTickSize(); delta = (tv.tv_usec-lastcounter)/ticksize; lastcounter = tv.tv_usec; #else #error Unknown operating system #endif cnow = clock(); randPoolAddBytes((byte const *)&cnow, sizeof(cnow)); tnow = time((time_t *)0); /* Read slowest clock last */ randPoolAddBytes((byte const *)&tnow, sizeof(tnow)); return delta; } Listing Seven /* randpool.h -- declarations for randpool.c */ #include "usuals.h" #define RANDPOOLBITS 3072 /* Whatever size you need (must be > 512) */ void randPoolStir(void); void randPoolAddBytes(byte const *buf, unsigned len); void randPoolGetBytes(byte *buf, unsigned len); byte randPoolGetByte(void); Listing Eight /* randpool.c -- True random number computation and storage * This is adapted from code in the Pretty Good Privacy (PGP) package. * Written by Colin Plumb. */ #include #include #include "randpool.h" #include "md5.h" #define RANDKEYWORDS 16 /* This is a parameter of the the MD5 algorithm */ /* The pool must be a multiple of the 16-byte (128-bit) MD5 block size */ #define RANDPOOLWORDS ((RANDPOOLBITS+127 & ~127) >> 5) #if RANDPOOLWORDS <= RANDKEYWORDS #error Random pool too small - please increase RANDPOOLBITS in randpool.h #endif /* Must be word-aligned, so make it words. Cast to bytes as needed. */ static word32 randPool[RANDPOOLWORDS]; /* Random pool */ static word32 randKey[RANDKEYWORDS]; /* Next stirring key */ static unsigned randPoolGetPos = sizeof(randPool); /* Position to get from */ static unsigned randKeyAddPos = 0; /* Position to add to */ /* "Stir in" any random seed material before removing any random bytes. */ void randPoolStir(void) { int i; word32 iv[4]; byteSwap(randPool, RANDPOOLWORDS); /* convert to word32s */ byteSwap(randKey, RANDKEYWORDS); /* Start IV from last block of randPool */ memcpy(iv, randPool+RANDPOOLWORDS-4, sizeof(iv)); /* CFB pass */ for (i = 0; i < RANDPOOLWORDS; i += 4) { MD5Transform(iv, randKey); iv[0] = randPool[i ] ^= iv[0]; iv[1] = randPool[i+1] ^= iv[1]; iv[2] = randPool[i+2] ^= iv[2]; iv[3] = randPool[i+3] ^= iv[3]; } memset(iv, 0, sizeof(iv)); /* Wipe IV from memory */ byteSwap(randPool, RANDPOOLWORDS); /* Convert back to bytes */ memcpy(randKey, randPool, sizeof(randKey)); /* Get new key */ randKeyAddPos = 0; /* Set up pointers for future use. */ randPoolGetPos = sizeof(randKey); } /* Make a deposit of information (entropy) into the pool. */ void randPoolAddBytes(byte const *buf, unsigned len) { byte *p = (byte *)randKey+randKeyAddPos; unsigned t = sizeof(randKey) - randKeyAddPos; while (len > t) { len -= t; while (t--) *p++ ^= *buf++; randPoolStir(); /* sets randKeyAddPos to 0 */ p = (byte *)randKey; t = sizeof(randKey); } if (len) { randKeyAddPos += len; do *p++ ^= *buf++; while (--len); randPoolGetPos = sizeof(randPool); /* Force stir on get */ } } /* Withdraw some bits from the pool. */ void randPoolGetBytes(byte *buf, unsigned len) { unsigned t; while (len > (t = sizeof(randPool) - randPoolGetPos)) { memcpy(buf, (byte const *)randPool+randPoolGetPos, t); buf += t; len -= t; randPoolStir(); } memcpy(buf, (byte const *)randPool+randPoolGetPos, len); randPoolGetPos += len; } /* Get a single byte */ byte randPoolGetByte(void) { if (randPoolGetPos == sizeof(randPool)) randPoolStir(); return ((byte const *)randPool)[randPoolGetPos++]; }