root/sys/libkern/arc4random.c
/*-
 * THE BEER-WARE LICENSE
 *
 * <dan@FreeBSD.ORG> wrote this file.  As long as you retain this notice you
 * can do whatever you want with this stuff.  If we meet some day, and you
 * think this stuff is worth it, you can buy me a beer in return.
 *
 * Dan Moschuk
 *
 * $FreeBSD: src/sys/libkern/arc4random.c,v 1.3.2.2 2001/09/17 07:06:50 silby Exp $
 */

#include <sys/types.h>
#include <sys/systm.h>
#include <sys/random.h>
#include <sys/libkern.h>
#include <sys/time.h>

#include <vm/vm_extern.h>

#define ARC4_MAXRUNS            16384
#define ARC4_RESEED_SECONDS     300
#define ARC4_KEYBYTES           32      /* 256 bit key */

struct arc4_data {
        uint8_t                 arc4_i;
        uint8_t                 arc4_j;
        int                     arc4_numruns;
        time_t                  arc4_nextreseed;
        uint8_t                 arc4_sbox[256];
};

static struct arc4_data         *arc4_data_pcpu[MAXCPU];

static uint8_t                  arc4_randbyte(struct arc4_data *);

static __inline void
arc4_swap(uint8_t *a, uint8_t *b)
{
        uint8_t c;

        c = *a;
        *a = *b;
        *b = c;
}

/*
 * Stir our S-box.
 */
static void
arc4_randomstir(struct arc4_data *d)
{
        uint8_t key[256];
        int r, n;

        /*
         * XXX read_random() returns unsafe numbers if the entropy
         * device is not loaded -- MarkM.
         */
        r = read_random(key, ARC4_KEYBYTES, 1);
        /* If r == 0 || -1, just use what was on the stack. */
        if (r > 0) {
                for (n = r; n < sizeof(key); n++)
                        key[n] = key[n % r];
        }

        for (n = 0; n < 256; n++) {
                d->arc4_j = (d->arc4_j + d->arc4_sbox[n] + key[n]) % 256;
                arc4_swap(&d->arc4_sbox[n], &d->arc4_sbox[d->arc4_j]);
        }

        /*
         * Discard early keystream, as per recommendations in:
         * "(Not So) Random Shuffles of RC4" by Ilya Mironov.
         */
        for (n = 0; n < 768 * 4; n++)
                arc4_randbyte(d);

        /* Reset for next reseed cycle. */
        d->arc4_nextreseed = time_uptime + ARC4_RESEED_SECONDS;
        d->arc4_numruns = 0;
}

/*
 * Generate a random byte.
 */
static uint8_t
arc4_randbyte(struct arc4_data *d)
{
        uint8_t arc4_t;

        d->arc4_i = (d->arc4_i + 1) % 256;
        d->arc4_j = (d->arc4_j + d->arc4_sbox[d->arc4_i]) % 256;

        arc4_swap(&d->arc4_sbox[d->arc4_i], &d->arc4_sbox[d->arc4_j]);

        arc4_t = (d->arc4_sbox[d->arc4_i] + d->arc4_sbox[d->arc4_j]) % 256;
        return d->arc4_sbox[arc4_t];
}

uint32_t
karc4random(void)
{
        struct arc4_data *d = arc4_data_pcpu[mycpuid];
        uint32_t ret;

        if (++(d->arc4_numruns) > ARC4_MAXRUNS ||
            time_uptime > d->arc4_nextreseed)
                arc4_randomstir(d);

        ret = arc4_randbyte(d);
        ret |= arc4_randbyte(d) << 8;
        ret |= arc4_randbyte(d) << 16;
        ret |= arc4_randbyte(d) << 24;

        return ret;
}

uint64_t
karc4random64(void)
{
        struct arc4_data *d = arc4_data_pcpu[mycpuid];
        uint64_t ret;

        if (++(d->arc4_numruns) > ARC4_MAXRUNS ||
            time_uptime > d->arc4_nextreseed)
                arc4_randomstir(d);

        ret = arc4_randbyte(d);
        ret |= arc4_randbyte(d) << 8;
        ret |= arc4_randbyte(d) << 16;
        ret |= arc4_randbyte(d) << 24;
        ret |= (uint64_t)arc4_randbyte(d) << 32;
        ret |= (uint64_t)arc4_randbyte(d) << 40;
        ret |= (uint64_t)arc4_randbyte(d) << 48;
        ret |= (uint64_t)arc4_randbyte(d) << 56;

        return ret;
}

void
karc4random_buf(void *ptr, size_t len)
{
        struct arc4_data *d = arc4_data_pcpu[mycpuid];
        uint8_t *p = ptr;

#if 0
        /* No one call this function in ISR/ithread. */
        crit_enter();
#endif

        if (++(d->arc4_numruns) > ARC4_MAXRUNS ||
            time_uptime > d->arc4_nextreseed)
                arc4_randomstir(d);

        while (len--)
                *p++ = arc4_randbyte(d);

#if 0
        crit_exit();
#endif
}

/*
 * Initialize our S-box to its beginning defaults.
 */
void
arc4_init_pcpu(int cpuid)
{
        struct arc4_data *d;
        int n;

        KASSERT(arc4_data_pcpu[cpuid] == NULL,
            ("arc4 was initialized on cpu%d", cpuid));

        d = (void *)kmem_alloc3(kernel_map, sizeof(*d), VM_SUBSYS_GD,
            KM_CPU(cpuid));
        memset(d, 0, sizeof(*d));

        for (n = 0; n < 256; n++)
                d->arc4_sbox[n] = (uint8_t)n;

        arc4_randomstir(d);

        /*
         * Discard early keystream, as per recommendations in:
         * "(Not So) Random Shuffles of RC4" by Ilya Mironov.
         */
        for (n = 0; n < 768 * 4; n++)
                arc4_randbyte(d);

        arc4_data_pcpu[cpuid] = d;
}