hackedteam/vector-default

View on GitHub
pe.old/SSHRAND.C

Summary

Maintainability
Test Coverage
/*
 * cryptographic random number generator for PuTTY's ssh client
 */

#include "putty.h"
#include "ssh.h"
#include <assert.h>

/* 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));

/*
 * `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;

    int stir_pending;
};

static struct RandPool pool;
int random_active = 0;
long next_noise_collection;

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);
    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 < sizeof(digest) / sizeof(*digest); k++)
        digest[k] ^= ((word32 *) (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 < sizeof(digest) / sizeof(*digest); k++)
        ((word32 *) (pool.pool + j))[k] = digest[k];
    }
    }

    /*
     * 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);

    pool.stir_pending = FALSE;
}

void random_add_noise(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((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;
    }

    for (i = 0; i < length; i++)
    pool.pool[i] ^= *p++;
    pool.poolpos = i;
}

static void random_timer(void *ctx, long now)
{
    if (random_active > 0 && now - next_noise_collection >= 0) {
    noise_regular();
    next_noise_collection =
        schedule_timer(NOISE_REGULAR_INTERVAL, random_timer, &pool);
    }
}

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();

    next_noise_collection =
        schedule_timer(NOISE_REGULAR_INTERVAL, random_timer, &pool);
    }

    random_active++;
}

void random_unref(void)
{
    random_active--;
    assert(random_active >= 0);
    if (random_active) return;

    expire_timer_context(&pool);
}

int random_byte(void)
{
    if (pool.poolpos >= POOLSIZE)
    random_stir();

    return pool.pool[pool.poolpos++];
}

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();
}