X-Git-Url: https://git.distorted.org.uk/~mdw/sgt/putty/blobdiff_plain/374330e25a6b51c40436fa869a381dd510790f6e..c16e353eb95120407b78602f0bbaedac57eccf44:/sshrand.c diff --git a/sshrand.c b/sshrand.c index 17ef6e34..c26fc757 100644 --- a/sshrand.c +++ b/sshrand.c @@ -2,7 +2,12 @@ * cryptographic random number generator for PuTTY's ssh client */ +#include "putty.h" #include "ssh.h" +#include + +/* Collect environmental noise every 5 minutes */ +#define NOISE_REGULAR_INTERVAL (5*60*TICKSPERSEC) void noise_get_heavy(void (*func) (void *, int)); void noise_get_light(void (*func) (void *, int)); @@ -36,34 +41,59 @@ struct RandPool { unsigned char incomingb[HASHINPUT]; int incomingpos; + + int stir_pending; }; static struct RandPool pool; +int random_active = 0; +long next_noise_collection; -void random_add_noise(void *noise, int length) { - unsigned char *p = noise; - - while (length >= (HASHINPUT - pool.incomingpos)) { - memcpy(pool.incomingb + pool.incomingpos, p, - HASHINPUT - pool.incomingpos); - p += HASHINPUT - pool.incomingpos; - length -= HASHINPUT - pool.incomingpos; - SHATransform((word32 *)pool.incoming, (word32 *)pool.incomingb); - pool.incomingpos = 0; - } - - memcpy(pool.incomingb, p, length); - pool.incomingpos = length; -} +#ifdef RANDOM_DIAGNOSTICS +int random_diagnostics = 0; +#endif -void random_stir(void) { - word32 block[HASHINPUT/sizeof(word32)]; - word32 digest[HASHSIZE/sizeof(word32)]; +static void random_stir(void) +{ + word32 block[HASHINPUT / sizeof(word32)]; + word32 digest[HASHSIZE / sizeof(word32)]; int i, j, k; + /* + * noise_get_light will call random_add_noise, which may call + * back to here. Prevent recursive stirs. + */ + if (pool.stir_pending) + return; + pool.stir_pending = TRUE; + noise_get_light(random_add_noise); - SHATransform((word32 *)pool.incoming, (word32 *)pool.incomingb); +#ifdef RANDOM_DIAGNOSTICS + { + int p, q; + printf("random stir starting\npool:\n"); + for (p = 0; p < POOLSIZE; p += HASHSIZE) { + printf(" "); + for (q = 0; q < HASHSIZE; q += 4) { + printf(" %08x", *(word32 *)(pool.pool + p + q)); + } + printf("\n"); + } + printf("incoming:\n "); + for (q = 0; q < HASHSIZE; q += 4) { + printf(" %08x", *(word32 *)(pool.incoming + q)); + } + printf("\nincomingb:\n "); + for (q = 0; q < HASHINPUT; q += 4) { + printf(" %08x", *(word32 *)(pool.incomingb + q)); + } + printf("\n"); + random_diagnostics++; + } +#endif + + SHATransform((word32 *) pool.incoming, (word32 *) pool.incomingb); pool.incomingpos = 0; /* @@ -92,14 +122,14 @@ void random_stir(void) { * things will be that much less predictable that way * round, when we subsequently return bytes ... */ - for (j = POOLSIZE; (j -= HASHSIZE) >= 0 ;) { + for (j = POOLSIZE; (j -= HASHSIZE) >= 0;) { /* * XOR the bit of the pool we're processing into the * digest. */ - for (k = 0; k < sizeof(digest)/sizeof(*digest); k++) - digest[k] ^= ((word32 *)(pool.pool+j))[k]; + for (k = 0; k < sizeof(digest) / sizeof(*digest); k++) + digest[k] ^= ((word32 *) (pool.pool + j))[k]; /* * Munge our unrevealed first block of the pool into @@ -111,9 +141,32 @@ void random_stir(void) { * Stick the result back into the pool. */ - for (k = 0; k < sizeof(digest)/sizeof(*digest); k++) - ((word32 *)(pool.pool+j))[k] = digest[k]; + for (k = 0; k < sizeof(digest) / sizeof(*digest); k++) + ((word32 *) (pool.pool + j))[k] = digest[k]; } + +#ifdef RANDOM_DIAGNOSTICS + if (i == 0) { + int p, q; + printf("random stir midpoint\npool:\n"); + for (p = 0; p < POOLSIZE; p += HASHSIZE) { + printf(" "); + for (q = 0; q < HASHSIZE; q += 4) { + printf(" %08x", *(word32 *)(pool.pool + p + q)); + } + printf("\n"); + } + printf("incoming:\n "); + for (q = 0; q < HASHSIZE; q += 4) { + printf(" %08x", *(word32 *)(pool.incoming + q)); + } + printf("\nincomingb:\n "); + for (q = 0; q < HASHINPUT; q += 4) { + printf(" %08x", *(word32 *)(pool.incomingb + q)); + } + printf("\n"); + } +#endif } /* @@ -121,49 +174,156 @@ void random_stir(void) { * there'll be some extra bizarreness there. */ SHATransform(digest, block); - memcpy(digest, pool.incoming, sizeof(digest)); + memcpy(pool.incoming, digest, sizeof(digest)); pool.poolpos = sizeof(pool.incoming); + + pool.stir_pending = FALSE; + +#ifdef RANDOM_DIAGNOSTICS + { + int p, q; + printf("random stir done\npool:\n"); + for (p = 0; p < POOLSIZE; p += HASHSIZE) { + printf(" "); + for (q = 0; q < HASHSIZE; q += 4) { + printf(" %08x", *(word32 *)(pool.pool + p + q)); + } + printf("\n"); + } + printf("incoming:\n "); + for (q = 0; q < HASHSIZE; q += 4) { + printf(" %08x", *(word32 *)(pool.incoming + q)); + } + printf("\nincomingb:\n "); + for (q = 0; q < HASHINPUT; q += 4) { + printf(" %08x", *(word32 *)(pool.incomingb + q)); + } + printf("\n"); + random_diagnostics--; + } +#endif } -static void random_add_heavynoise(void *noise, int length) { +void random_add_noise(void *noise, int length) +{ unsigned char *p = noise; + int i; - while (length >= (POOLSIZE - pool.poolpos)) { - memcpy(pool.pool + pool.poolpos, p, POOLSIZE - pool.poolpos); - p += POOLSIZE - pool.poolpos; - length -= POOLSIZE - pool.poolpos; + if (!random_active) + return; + + /* + * This function processes HASHINPUT bytes into only HASHSIZE + * bytes, so _if_ we were getting incredibly high entropy + * sources then we would be throwing away valuable stuff. + */ + while (length >= (HASHINPUT - pool.incomingpos)) { + memcpy(pool.incomingb + pool.incomingpos, p, + HASHINPUT - pool.incomingpos); + p += HASHINPUT - pool.incomingpos; + length -= HASHINPUT - pool.incomingpos; + SHATransform((word32 *) pool.incoming, (word32 *) pool.incomingb); + for (i = 0; i < HASHSIZE; i++) { + pool.pool[pool.poolpos++] ^= pool.incomingb[i]; + if (pool.poolpos >= POOLSIZE) + pool.poolpos = 0; + } + if (pool.poolpos < HASHSIZE) + random_stir(); + + pool.incomingpos = 0; + } + + memcpy(pool.incomingb + pool.incomingpos, p, length); + pool.incomingpos += length; +} + +void random_add_heavynoise(void *noise, int length) +{ + unsigned char *p = noise; + int i; + + while (length >= POOLSIZE) { + for (i = 0; i < POOLSIZE; i++) + pool.pool[i] ^= *p++; + random_stir(); + length -= POOLSIZE; + } + + for (i = 0; i < length; i++) + pool.pool[i] ^= *p++; + random_stir(); +} + +static void random_add_heavynoise_bitbybit(void *noise, int length) +{ + unsigned char *p = noise; + int i; + + while (length >= POOLSIZE - pool.poolpos) { + for (i = 0; i < POOLSIZE - pool.poolpos; i++) + pool.pool[pool.poolpos + i] ^= *p++; random_stir(); + length -= POOLSIZE - pool.poolpos; pool.poolpos = 0; } - memcpy(pool.pool, p, length); - pool.poolpos = length; + for (i = 0; i < length; i++) + pool.pool[i] ^= *p++; + pool.poolpos = i; } -void random_init(void) { - memset(&pool, 0, sizeof(pool)); /* just to start with */ +static void random_timer(void *ctx, unsigned long now) +{ + if (random_active > 0 && now == next_noise_collection) { + noise_regular(); + next_noise_collection = + schedule_timer(NOISE_REGULAR_INTERVAL, random_timer, &pool); + } +} - /* - * For noise_get_heavy, we temporarily use `poolpos' as the - * pointer for addition of noise, rather than extraction of - * random numbers. - */ - pool.poolpos = 0; - noise_get_heavy(random_add_heavynoise); +void random_ref(void) +{ + if (!random_active) { + memset(&pool, 0, sizeof(pool)); /* just to start with */ - random_stir(); + random_active++; + + noise_get_heavy(random_add_heavynoise_bitbybit); + random_stir(); + + next_noise_collection = + schedule_timer(NOISE_REGULAR_INTERVAL, random_timer, &pool); + } } -int random_byte(void) { +void random_unref(void) +{ + assert(random_active > 0); + if (random_active == 1) { + random_save_seed(); + expire_timer_context(&pool); + } + random_active--; +} + +int random_byte(void) +{ + assert(random_active); + if (pool.poolpos >= POOLSIZE) random_stir(); return pool.pool[pool.poolpos++]; } -void random_get_savedata(void **data, int *len) { +void random_get_savedata(void **data, int *len) +{ + void *buf = snewn(POOLSIZE / 2, char); + random_stir(); + memcpy(buf, pool.pool + pool.poolpos, POOLSIZE / 2); + *len = POOLSIZE / 2; + *data = buf; random_stir(); - *data = pool.pool+pool.poolpos; - *len = POOLSIZE/2; }