瀏覽代碼

PuTTY snapshot 320bf847 (Replace PuTTY's PRNG with a Fortuna-like system - 2019-01-24)

Source commit: 9fa11036314af7bc97bb32047bf5c9d596da8ffa
Martin Prikryl 6 年之前
父節點
當前提交
c9b8439c12
共有 6 個文件被更改,包括 391 次插入304 次删除
  1. 1 0
      source/putty/defs.h
  2. 6 0
      source/putty/putty.h
  3. 28 4
      source/putty/ssh.h
  4. 286 0
      source/putty/sshprng.c
  5. 61 280
      source/putty/sshrand.c
  6. 9 20
      source/putty/windows/winnoise.c

+ 1 - 0
source/putty/defs.h

@@ -90,6 +90,7 @@ typedef struct PortFwdManager PortFwdManager;
 typedef struct PortFwdRecord PortFwdRecord;
 typedef struct ConnectionLayer ConnectionLayer;
 
+typedef struct prng prng;
 typedef struct ssh_hashalg ssh_hashalg;
 typedef struct ssh_hash ssh_hash;
 typedef struct ssh_kex ssh_kex;

+ 6 - 0
source/putty/putty.h

@@ -1702,6 +1702,12 @@ extern int random_active;
  * calls random_ref on startup and random_unref on shutdown. */
 void random_ref(void);
 void random_unref(void);
+/* random_setup_special is used by PuTTYgen. It makes an extra-big
+ * random number generator. */
+void random_setup_special();
+/* Manually drop a random seed into the random number generator, e.g.
+ * just before generating a key. */
+void random_reseed(ptrlen seed);
 
 /*
  * Exports from pinger.c.

+ 28 - 4
source/putty/ssh.h

@@ -867,8 +867,6 @@ extern const char sshver[];
  */
 extern bool ssh_fallback_cmd(Backend *backend);
 
-void SHATransform(uint32_t *digest, uint32_t *data);
-
 /*
  * Check of compiler version
  */
@@ -892,9 +890,35 @@ void SHATransform(uint32_t *digest, uint32_t *data);
 #   undef COMPILER_SUPPORTS_SHA_NI
 #endif
 
+/*
+ * The PRNG type, defined in sshprng.c. Visible data fields are
+ * 'savesize', which suggests how many random bytes you should request
+ * from a particular PRNG instance to write to putty.rnd, and a
+ * BinarySink implementation which you can use to write seed data in
+ * between calling prng_seed_{begin,finish}.
+ */
+struct prng {
+    size_t savesize;
+    BinarySink_IMPLEMENTATION;
+    /* (also there's a surrounding implementation struct in sshprng.c) */
+};
+prng *prng_new(const ssh_hashalg *hashalg);
+void prng_free(prng *p);
+void prng_seed_begin(prng *p);
+void prng_seed_finish(prng *p);
+void prng_read(prng *p, void *vout, size_t size);
+void prng_add_entropy(prng *p, unsigned source_id, ptrlen data);
+
+/* This function must be implemented by the platform, and returns a
+ * timer in milliseconds that the PRNG can use to know whether it's
+ * been reseeded too recently to do it again.
+ *
+ * The PRNG system has its own special timing function not because its
+ * timing needs are unusual in the real applications, but simply so
+ * that testcrypt can mock it to keep the tests deterministic. */
+uint64_t prng_reseed_time_ms(void);
+
 void random_read(void *out, size_t size);
-void random_add_noise(void *noise, int length);
-void random_add_heavynoise(void *noise, int length);
 
 /* Exports from x11fwd.c */
 enum {

+ 286 - 0
source/putty/sshprng.c

@@ -0,0 +1,286 @@
+/*
+ * sshprng.c: PuTTY's cryptographic pseudorandom number generator.
+ *
+ * This module just defines the PRNG object type and its methods. The
+ * usual global instance of it is managed by sshrand.c.
+ */
+
+#include "putty.h"
+#include "ssh.h"
+#include "mpint.h"
+
+#ifdef PRNG_DIAGNOSTICS
+#define prngdebug debug
+#else
+#define prngdebug(...) ((void)0)
+#endif
+
+/*
+ * This random number generator is based on the 'Fortuna' design by
+ * Niels Ferguson and Bruce Schneier. The biggest difference is that I
+ * use SHA-256 in place of a block cipher: the generator side of the
+ * system works by computing HASH(key || counter) instead of
+ * ENCRYPT(counter, key).
+ *
+ * Rationale: the Fortuna description itself suggests that using
+ * SHA-256 would be nice but people wouldn't accept it because it's
+ * too slow - but PuTTY isn't a heavy enough user of random numbers to
+ * make that a serious worry. In fact even with SHA-256 this generator
+ * is faster than the one we previously used. Also the Fortuna
+ * description worries about periodic rekeying to avoid the barely
+ * detectable pattern of never repeating a cipher block - but with
+ * SHA-256, even that shouldn't be a worry, because the output
+ * 'blocks' are twice the size, and also SHA-256 has no guarantee of
+ * bijectivity, so it surely _could_ be possible to generate the same
+ * block from two counter values. Thirdly, Fortuna has to have a hash
+ * function anyway, for reseeding and entropy collection, so reusing
+ * the same one means it only depends on one underlying primitive and
+ * can be easily reinstantiated with a larger hash function if you
+ * decide you'd like to do that on a particular occasion.
+ */
+
+#define NCOLLECTORS 32
+#define RESEED_DATA_SIZE 64
+
+typedef struct prng_impl prng_impl;
+struct prng_impl {
+    prng Prng;
+
+    const ssh_hashalg *hashalg;
+
+    /*
+     * Generation side:
+     *
+     * 'generator' is a hash object with the current key preloaded
+     * into it. The counter-mode generation is achieved by copying
+     * that hash object, appending the counter value to the copy, and
+     * calling ssh_hash_final.
+     *
+     * pending_output is a buffer of size equal to the hash length,
+     * which receives each of those hashes as it's generated. The
+     * bytes of the hash are returned in reverse order, just because
+     * that made it marginally easier to deal with the
+     * pending_output_remaining field.
+     */
+    ssh_hash *generator;
+    mp_int *counter;
+    uint8_t *pending_output;
+    size_t pending_output_remaining;
+
+    /*
+     * When re-seeding the generator, you call prng_seed_begin(),
+     * which sets up a hash object in 'keymaker'. You write your new
+     * seed data into it (which you can do by calling put_data on the
+     * PRNG object itself) and then call prng_seed_finish(), which
+     * finalises this hash and uses the output to set up the new
+     * generator.
+     *
+     * The keymaker hash preimage includes the previous key, so if you
+     * just want to change keys for the sake of not keeping the same
+     * one for too long, you don't have to put any extra seed data in
+     * at all.
+     */
+    ssh_hash *keymaker;
+
+    /*
+     * Collection side:
+     *
+     * There are NCOLLECTORS hash objects collecting entropy. Each
+     * separately numbered entropy source puts its output into those
+     * hash objects in the order 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,...,
+     * that is to say, each entropy source has a separate counter
+     * which is incremented every time that source generates an event,
+     * and the event data is added to the collector corresponding to
+     * the index of the lowest set bit in the current counter value.
+     *
+     * Whenever collector #0 has at least RESEED_DATA_SIZE bytes (and
+     * it's not at least 100ms since the last reseed), the PRNG is
+     * reseeded, with seed data on reseed #n taken from the first j
+     * collectors, where j is one more than the number of factors of 2
+     * in n. That is, collector #0 is used in every reseed; #1 in
+     * every other one, #2 in every fourth, etc.
+     *
+     * 'until_reseed' counts the amount of data that still needs to be
+     * added to collector #0 before a reseed will be triggered.
+     */
+    uint32_t source_counters[NOISE_MAX_SOURCES];
+    ssh_hash *collectors[NCOLLECTORS];
+    size_t until_reseed;
+    uint32_t reseeds;
+    uint64_t last_reseed_time;
+};
+
+static void prng_seed_BinarySink_write(
+    BinarySink *bs, const void *data, size_t len);
+
+prng *prng_new(const ssh_hashalg *hashalg)
+{
+    prng_impl *pi = snew(prng_impl);
+
+    memset(pi, 0, sizeof(prng_impl));
+    pi->hashalg = hashalg;
+    pi->keymaker = NULL;
+    pi->generator = NULL;
+    pi->pending_output = snewn(pi->hashalg->hlen, uint8_t);
+    pi->pending_output_remaining = 0;
+    pi->counter = mp_new(128);
+    for (size_t i = 0; i < NCOLLECTORS; i++)
+        pi->collectors[i] = ssh_hash_new(pi->hashalg);
+    pi->until_reseed = 0;
+    BinarySink_INIT(&pi->Prng, prng_seed_BinarySink_write);
+
+    pi->Prng.savesize = pi->hashalg->hlen * 4;
+
+    return &pi->Prng;
+}
+
+void prng_free(prng *pr)
+{
+    prng_impl *pi = container_of(pr, prng_impl, Prng);
+
+    sfree(pi->pending_output);
+    mp_free(pi->counter);
+    for (size_t i = 0; i < NCOLLECTORS; i++)
+        ssh_hash_free(pi->collectors[i]);
+    if (pi->generator)
+        ssh_hash_free(pi->generator);
+    if (pi->keymaker)
+        ssh_hash_free(pi->keymaker);
+    smemclr(pi, sizeof(*pi));
+    sfree(pi);
+}
+
+void prng_seed_begin(prng *pr)
+{
+    prng_impl *pi = container_of(pr, prng_impl, Prng);
+
+    assert(!pi->keymaker);
+
+    prngdebug("prng: reseed begin\n");
+
+    /*
+     * Make a hash instance that will generate the key for the new one.
+     */
+    if (pi->generator) {
+        pi->keymaker = pi->generator;
+        pi->generator = NULL;
+    } else {
+        pi->keymaker = ssh_hash_new(pi->hashalg);
+    }
+
+    put_byte(pi->keymaker, 'R');
+}
+
+static void prng_seed_BinarySink_write(
+    BinarySink *bs, const void *data, size_t len)
+{
+    prng *pr = BinarySink_DOWNCAST(bs, prng);
+    prng_impl *pi = container_of(pr, prng_impl, Prng);
+    assert(pi->keymaker);
+    prngdebug("prng: got %zu bytes of seed\n", len);
+    put_data(pi->keymaker, data, len);
+}
+
+void prng_seed_finish(prng *pr)
+{
+    prng_impl *pi = container_of(pr, prng_impl, Prng);
+
+    assert(pi->keymaker);
+
+    prngdebug("prng: reseed finish\n");
+
+    /*
+     * Actually generate the key.
+     */
+    ssh_hash_final(pi->keymaker, pi->pending_output);
+    pi->keymaker = NULL;
+
+    /*
+     * Load that key into a fresh hash instance, which will become the
+     * new generator.
+     */
+    assert(!pi->generator);
+    pi->generator = ssh_hash_new(pi->hashalg);
+    put_data(pi->generator, pi->pending_output, pi->hashalg->hlen);
+    smemclr(pi->pending_output, pi->hashalg->hlen);
+
+    pi->until_reseed = RESEED_DATA_SIZE;
+    pi->last_reseed_time = prng_reseed_time_ms();
+    pi->pending_output_remaining = 0;
+}
+
+static inline void prng_generate(prng_impl *pi)
+{
+    ssh_hash *h = ssh_hash_copy(pi->generator);
+
+    prngdebug("prng_generate\n");
+
+    put_byte(h, 'G');
+    put_mp_ssh2(h, pi->counter);
+    mp_add_integer_into(pi->counter, pi->counter, 1);
+    ssh_hash_final(h, pi->pending_output);
+    pi->pending_output_remaining = pi->hashalg->hlen;
+}
+
+void prng_read(prng *pr, void *vout, size_t size)
+{
+    prng_impl *pi = container_of(pr, prng_impl, Prng);
+
+    assert(!pi->keymaker);
+
+    prngdebug("prng_read %zu\n", size);
+
+    uint8_t *out = (uint8_t *)vout;
+    for (; size > 0; size--) {
+        if (pi->pending_output_remaining == 0)
+            prng_generate(pi);
+        pi->pending_output_remaining--;
+        *out++ = pi->pending_output[pi->pending_output_remaining];
+        pi->pending_output[pi->pending_output_remaining] = 0;
+    }
+
+    prng_seed_begin(&pi->Prng);
+    prng_seed_finish(&pi->Prng);
+}
+
+void prng_add_entropy(prng *pr, unsigned source_id, ptrlen data)
+{
+    prng_impl *pi = container_of(pr, prng_impl, Prng);
+
+    assert(source_id < NOISE_MAX_SOURCES);
+    uint32_t counter = ++pi->source_counters[source_id];
+
+    size_t index = 0;
+    while (index+1 < NCOLLECTORS && !(counter & 1)) {
+        counter >>= 1;
+        index++;
+    }
+
+    prngdebug("prng_add_entropy source=%u size=%zu -> collector %zi\n",
+              source_id, data.len, index);
+
+    put_datapl(pi->collectors[index], data);
+
+    if (index == 0)
+        pi->until_reseed = (pi->until_reseed < data.len ? 0 :
+                            pi->until_reseed - data.len);
+
+    if (pi->until_reseed == 0 &&
+        prng_reseed_time_ms() - pi->last_reseed_time >= 100) {
+        prng_seed_begin(&pi->Prng);
+
+        uint32_t reseed_index = ++pi->reseeds;
+        prngdebug("prng entropy reseed #%"PRIu32"\n", reseed_index);
+        for (size_t i = 0; i < NCOLLECTORS; i++) {
+            prngdebug("emptying collector %zu\n", i);
+            ssh_hash_final(pi->collectors[i], pi->pending_output);
+            put_data(&pi->Prng, pi->pending_output, pi->hashalg->hlen);
+            pi->collectors[i] = ssh_hash_new(pi->hashalg);
+            if (reseed_index & 1)
+                break;
+            reseed_index >>= 1;
+        }
+
+        prng_seed_finish(&pi->Prng);
+    }
+}

+ 61 - 280
source/putty/sshrand.c

@@ -1,5 +1,5 @@
 /*
- * cryptographic random number generator for PuTTY's ssh client
+ * sshrand.c: manage the global live PRNG instance.
  */
 
 #include "putty.h"
@@ -9,305 +9,92 @@
 /* Collect environmental noise every 5 minutes */
 #define NOISE_REGULAR_INTERVAL (5*60*TICKSPERSEC)
 
-/*
- * `pool' itself is a pool of random data which we actually use: we
- * return bytes from `pool', at position `poolpos', until `poolpos'
- * reaches the end of the pool. At this point we generate more
- * random data, by adding noise, stirring well, and resetting
- * `poolpos' to point to just past the beginning of the pool (not
- * _the_ beginning, since otherwise we'd give away the whole
- * contents of our pool, and attackers would just have to guess the
- * next lot of noise).
- *
- * `incomingb' buffers acquired noise data, until it gets full, at
- * which point the acquired noise is SHA'ed into `incoming' and
- * `incomingb' is cleared. The noise in `incoming' is used as part
- * of the noise for each stirring of the pool, in addition to local
- * time, process listings, and other such stuff.
- */
-
-#define HASHINPUT 64		       /* 64 bytes SHA input */
-#define HASHSIZE 20		       /* 160 bits SHA output */
-#define POOLSIZE 1200		       /* size of random pool */
-
-struct RandPool {
-    unsigned char pool[POOLSIZE];
-    int poolpos;
-
-    unsigned char incoming[HASHSIZE];
-
-    unsigned char incomingb[HASHINPUT];
-    int incomingpos;
-
-    bool stir_pending;
-};
-
 int random_active = 0;
 
 #ifdef FUZZING
+
 /*
  * Special dummy version of the RNG for use when fuzzing.
  */
-void random_add_noise(void *noise, int length) { }
-void random_add_heavynoise(void *noise, int length) { }
+void random_add_noise(NoiseSourceId source, const void *noise, int length) { }
 void random_ref(void) { }
+void random_setup_special(void) { }
 void random_unref(void) { }
 void random_read(void *out, size_t size)
 {
-    return 0x45; /* Chosen by eight fair coin tosses */
     memset(out, 0x45, size); /* Chosen by eight fair coin tosses */
 }
 void random_get_savedata(void **data, int *len) { }
-#else /* !FUZZING */
-static struct RandPool pool;
-long next_noise_collection;
-
-#ifdef RANDOM_DIAGNOSTICS
-int random_diagnostics = 0;
-#endif
-
-static void random_stir(void)
-{
-    uint32_t block[HASHINPUT / sizeof(uint32_t)];
-    uint32_t digest[HASHSIZE / sizeof(uint32_t)];
-    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);
 
-#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", *(uint32_t *)(pool.pool + p + q));            
-            }
-            printf("\n");
-        }
-        printf("incoming:\n   ");
-        for (q = 0; q < HASHSIZE; q += 4) {
-            printf(" %08x", *(uint32_t *)(pool.incoming + q));
-        }
-        printf("\nincomingb:\n   ");
-        for (q = 0; q < HASHINPUT; q += 4) {
-            printf(" %08x", *(uint32_t *)(pool.incomingb + q));
-        }
-        printf("\n");
-        random_diagnostics++;
-    }
-#endif
-
-    SHATransform((uint32_t *) pool.incoming, (uint32_t *) pool.incomingb);
-    pool.incomingpos = 0;
-
-    /*
-     * Chunks of this code are blatantly endianness-dependent, but
-     * as it's all random bits anyway, WHO CARES?
-     */
-    memcpy(digest, pool.incoming, sizeof(digest));
-
-    /*
-     * Make two passes over the pool.
-     */
-    for (i = 0; i < 2; i++) {
-
-	/*
-	 * We operate SHA in CFB mode, repeatedly adding the same
-	 * block of data to the digest. But we're also fiddling
-	 * with the digest-so-far, so this shouldn't be Bad or
-	 * anything.
-	 */
-	memcpy(block, pool.pool, sizeof(block));
-
-	/*
-	 * Each pass processes the pool backwards in blocks of
-	 * HASHSIZE, just so that in general we get the output of
-	 * SHA before the corresponding input, in the hope that
-	 * things will be that much less predictable that way
-	 * round, when we subsequently return bytes ...
-	 */
-	for (j = POOLSIZE; (j -= HASHSIZE) >= 0;) {
-	    /*
-	     * XOR the bit of the pool we're processing into the
-	     * digest.
-	     */
-
-	    for (k = 0; k < lenof(digest); k++)
-		digest[k] ^= ((uint32_t *) (pool.pool + j))[k];
-
-	    /*
-	     * Munge our unrevealed first block of the pool into
-	     * it.
-	     */
-	    SHATransform(digest, block);
-
-	    /*
-	     * Stick the result back into the pool.
-	     */
-
-	    for (k = 0; k < lenof(digest); k++)
-		((uint32_t *) (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", *(uint32_t *)(pool.pool + p + q));
-                }
-                printf("\n");
-            }
-            printf("incoming:\n   ");
-            for (q = 0; q < HASHSIZE; q += 4) {
-                printf(" %08x", *(uint32_t *)(pool.incoming + q));
-            }
-            printf("\nincomingb:\n   ");
-            for (q = 0; q < HASHINPUT; q += 4) {
-                printf(" %08x", *(uint32_t *)(pool.incomingb + q));
-            }
-            printf("\n");
-        }
-#endif
-    }
-
-    /*
-     * Might as well save this value back into `incoming', just so
-     * there'll be some extra bizarreness there.
-     */
-    SHATransform(digest, block);
-    memcpy(pool.incoming, digest, sizeof(digest));
-
-    pool.poolpos = sizeof(pool.incoming);
+#else /* !FUZZING */
 
-    pool.stir_pending = false;
+/* Dummy structure for the sake of having something to expire_timer_context */
+static struct random_timer_context { int dummy; } random_timer_ctx;
 
-#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", *(uint32_t *)(pool.pool + p + q));            
-            }
-            printf("\n");
-        }
-        printf("incoming:\n   ");
-        for (q = 0; q < HASHSIZE; q += 4) {
-            printf(" %08x", *(uint32_t *)(pool.incoming + q));
-        }
-        printf("\nincomingb:\n   ");
-        for (q = 0; q < HASHINPUT; q += 4) {
-            printf(" %08x", *(uint32_t *)(pool.incomingb + q));
-        }
-        printf("\n");
-        random_diagnostics--;
-    }
-#endif
-}
+static prng *global_prng;
+static unsigned long next_noise_collection;
 
-void random_add_noise(void *noise, int length)
+void random_add_noise(NoiseSourceId source, const void *noise, int length)
 {
-    unsigned char *p = noise;
-    int i;
-
     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((uint32_t *) pool.incoming, (uint32_t *) pool.incomingb);
-	for (i = 0; i < HASHSIZE; i++) {
-	    pool.pool[pool.poolpos++] ^= pool.incoming[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;
+    prng_add_entropy(global_prng, source, make_ptrlen(noise, length));
 }
 
-void random_add_heavynoise(void *noise, int length)
+static void random_timer(void *ctx, unsigned long now)
 {
-    unsigned char *p = noise;
-    int i;
-
-    while (length >= POOLSIZE) {
-	for (i = 0; i < POOLSIZE; i++)
-	    pool.pool[i] ^= *p++;
-	random_stir();
-	length -= POOLSIZE;
+    if (random_active > 0 && now == next_noise_collection) {
+	noise_regular();
+	next_noise_collection =
+	    schedule_timer(NOISE_REGULAR_INTERVAL, random_timer,
+                           &random_timer_ctx);
     }
-
-    for (i = 0; i < length; i++)
-	pool.pool[i] ^= *p++;
-    random_stir();
 }
 
-static void random_add_heavynoise_bitbybit(void *noise, int length)
+static void random_seed_callback(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;
-    }
-
-    for (i = 0; i < length; i++)
-	pool.pool[i] ^= *p++;
-    pool.poolpos = i;
+    put_data(global_prng, noise, length);
 }
 
-static void random_timer(void *ctx, unsigned long now)
+static void random_create(const ssh_hashalg *hashalg)
 {
-    if (random_active > 0 && now == next_noise_collection) {
-	noise_regular();
-	next_noise_collection =
-	    schedule_timer(NOISE_REGULAR_INTERVAL, random_timer, &pool);
-    }
+    assert(!global_prng);
+    global_prng = prng_new(hashalg);
+
+    prng_seed_begin(global_prng);
+    noise_get_heavy(random_seed_callback);
+    prng_seed_finish(global_prng);
+
+    next_noise_collection =
+        schedule_timer(NOISE_REGULAR_INTERVAL, random_timer,
+                       &random_timer_ctx);
+
+    /* noise_get_heavy probably read our random seed file.
+     * Therefore (in fact, even if it didn't), we should write a
+     * fresh one, in case another instance of ourself starts up
+     * before we finish, and also in case an attacker gets hold of
+     * the seed data we used. */
+    random_save_seed();
 }
 
 void random_ref(void)
 {
-    if (!random_active) {
-	memset(&pool, 0, sizeof(pool));    /* just to start with */
-
-	noise_get_heavy(random_add_heavynoise_bitbybit);
-	random_stir();
+    if (!random_active++)
+        random_create(&ssh_sha256);
+}
 
-	next_noise_collection =
-	    schedule_timer(NOISE_REGULAR_INTERVAL, random_timer, &pool);
-    }
+void random_setup_special()
+{
     random_active++;
+    random_create(&ssh_sha512);
+}
+
+void random_reseed(ptrlen seed)
+{
+    prng_seed_begin(global_prng);
+    put_datapl(global_prng, seed);
+    prng_seed_finish(global_prng);
 }
 
 void random_unref(void)
@@ -315,31 +102,25 @@ void random_unref(void)
     assert(random_active > 0);
     if (random_active == 1) {
         random_save_seed();
-        expire_timer_context(&pool);
+        expire_timer_context(&random_timer_ctx);
+        prng_free(global_prng);
+        global_prng = NULL;
     }
     random_active--;
 }
 
-void random_read(void *vout, size_t size)
+void random_read(void *buf, size_t size)
 {
-    assert(random_active);
-
-    uint8_t *out = (uint8_t *)vout;
-    while (size-- > 0) {
-        if (pool.poolpos >= POOLSIZE)
-            random_stir();
-
-        *out++ = pool.pool[pool.poolpos++];
-    }
+    assert(random_active > 0);
+    prng_read(global_prng, buf, size);
 }
 
 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;
+    void *buf = snewn(global_prng->savesize, char);
+    random_read(buf, global_prng->savesize);
+    *len = global_prng->savesize;
     *data = buf;
-    random_stir();
 }
-#endif
+
+#endif /* FUZZING */

+ 9 - 20
source/putty/windows/winnoise.c

@@ -73,8 +73,6 @@ void noise_get_heavy(void (*func) (void *, int))
     }
 
     read_random_seed(func);
-    /* Update the seed immediately, in case another instance uses it. */
-    random_save_seed();
 }
 
 void random_save_seed(void)
@@ -89,24 +87,6 @@ void random_save_seed(void)
     }
 }
 
-/*
- * This function is called every time the random pool needs
- * stirring, and will acquire the system time in all available
- * forms.
- */
-void noise_get_light(void (*func) (void *, int))
-{
-    SYSTEMTIME systime;
-    DWORD adjust[2];
-    BOOL rubbish;
-
-    GetSystemTime(&systime);
-    func(&systime, sizeof(systime));
-
-    GetSystemTimeAdjustment(&adjust[0], &adjust[1], &rubbish);
-    func(&adjust, sizeof(adjust));
-}
-
 /*
  * This function is called on a timer, and it will monitor
  * frequently changing quantities such as the state of physical and
@@ -163,3 +143,12 @@ void noise_ultralight(NoiseSourceId id, unsigned long data)
     if (QueryPerformanceCounter(&perftime))
 	random_add_noise(NOISE_SOURCE_PERFCOUNT, &perftime, sizeof(perftime));
 }
+
+uint64_t prng_reseed_time_ms(void)
+{
+    FILETIME ft;
+    GetSystemTimeAsFileTime(&ft);
+    uint64_t value = ft.dwHighDateTime;
+    value = (value << 32) + ft.dwLowDateTime;
+    return value / 10000;              /* 1 millisecond / 100ns */
+}