#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
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;
}
static void
arc4_randomstir(struct arc4_data *d)
{
uint8_t key[256];
int r, n;
r = read_random(key, ARC4_KEYBYTES, 1);
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]);
}
for (n = 0; n < 768 * 4; n++)
arc4_randbyte(d);
d->arc4_nextreseed = time_uptime + ARC4_RESEED_SECONDS;
d->arc4_numruns = 0;
}
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
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
}
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);
for (n = 0; n < 768 * 4; n++)
arc4_randbyte(d);
arc4_data_pcpu[cpuid] = d;
}