/* * ppg_random.c 1995-08-29 * Michael Shields * Public domain * * A generic interface to obtaining the strongest available source * of random numbers. If you #define PATH_PGP, it will use its * +makerandom option to access its cryptographic generator; * otherwise, we just use random(3) or rand(3) and hope for the best. * * These functions are not reentrant. * * If you are optimizing, you might consider making these routines * more conservative of entropy. * */ #include #include #include #include #include #include #ifdef STDC_HEADERS #include #endif #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_MALLOC_H #include #endif #include "ppg_random.h" /* The entropy cache. We actually access this in bytes, not bit-by-bit; this is a bit wasteful but simplifies things. */ static unsigned char *cache; /* The original head of the cache, in case we need to free() it. */ static unsigned char *cachehead = NULL; /* Cache size, in bytes. */ static int cachesize = 0; /* Cache increment, used to magically enlarge it if necessary. */ static int cacheinc; static int pgp_rand_really_init __P((void)); int ppg_rand_init(chunks, bits) int chunks; int bits; { assert(chunks > 0); assert(bits > 0); if (cachehead) free(cachehead); /* Double what they asked for, to be conservative about modulo lossage. */ cacheinc = cachesize = chunks * (bits + 7) / 8 * 2; cachehead = cache = malloc(cacheinc); if (!cache) { cachesize = 0; return 1; } if (pgp_rand_really_init()) { free(cachehead); cachesize = 0; return 1; } return 0; } static int pgp_rand_really_init() { #ifdef PATH_PGP char pgpcommand[1024]; char *pgptmpname; int pgptmpfd; int retval = 0; pgptmpname = tmpnam(NULL); if (!pgptmpname) return 1; sprintf(pgpcommand, "%s +verbose=0 +makerandom=%d %s > /dev/null", PATH_PGP, cachesize, pgptmpname); if (system(pgpcommand)) return 1; pgptmpfd = open(pgptmpname, O_RDONLY); if (pgptmpfd < 0 || read(pgptmpfd, cache, cachesize) < 0) retval = 1; if (pgptmpfd >= 0) close(pgptmpfd); /* Hey, why ca'n't PGP output random bytes to stdout? */ sprintf(pgpcommand, "%s +verbose=0 -w %s > /dev/null", PATH_PGP, pgptmpname); system(pgpcommand); return retval; #else /* not PATH_PGP */ int i; srandomdev(); for (i = 0; i < cachesize; i++) { /* Conservatively, take what RAND() returns and fold it down to one character. */ long r = RAND(); unsigned char *cp; cache[i] = 0; for (cp = (unsigned char *) &r; cp < (unsigned char *) &r + sizeof(r); cp++) cache[i] ^= *cp; } return 0; #endif /* not PATH_PGP */ } int ppg_rand(max) int max; { /* Number of bytes we'll need to pull off the cache. */ int bytes = 1; unsigned int t; int i; assert(max > 0); t = 255; while (t < max) { bytes++; t <<= 8; t |= 255; } do { if (cachesize < bytes) /* Get more entropy, twice as much as before. */ ppg_rand_init(2, cacheinc * 8); cachesize -= bytes; t = 0; for (i = 0; i < bytes; i++) t |= *cache++ << (i * 8); } while (t > max); return (t); }