#include <sys/types.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/poll.h>
#include <sys/event.h>
#include <sys/random.h>
#include <sys/systimer.h>
#include <sys/time.h>
#include <sys/proc.h>
#include <sys/lock.h>
#include <sys/sysctl.h>
#include <sys/sysmsg.h>
#include <sys/spinlock.h>
#include <sys/csprng.h>
#include <sys/malloc.h>
#include <machine/atomic.h>
#include <machine/clock.h>
#include <sys/spinlock2.h>
#include <sys/signal2.h>
static struct csprng_state *csprng_pcpu;
static struct csprng_state csprng_boot;
static MALLOC_DEFINE(M_NRANDOM, "nrandom", "csprng");
static int add_buffer_randomness_state(struct csprng_state *state,
const char *buf, int bytes, int srcid);
static
struct csprng_state *
iterate_csprng_state(int bytes __unused)
{
static unsigned int csprng_iterator;
unsigned int n;
if (csprng_pcpu) {
n = csprng_iterator++ % ncpus;
return &csprng_pcpu[n];
}
return NULL;
}
typedef u_int32_t u4;
static void IBAA
(
u4 *m,
u4 *r,
u4 *aa,
u4 *bb,
u4 *counter
)
{
u4 a, b, x, y, i;
a = *aa;
b = *bb + *counter;
++*counter;
for (i = 0; i < SIZE; ++i) {
x = m[i];
a = barrel(a) + m[ind(i + (SIZE / 2))];
m[i] = y = m[ind(x)] + a + b;
r[i] = b = m[ind(y >> ALPHA)] + x;
}
*bb = b; *aa = a;
}
static void IBAA_Init(struct ibaa_state *ibaa);
static void IBAA_Call(struct ibaa_state *ibaa);
static void IBAA_Seed(struct ibaa_state *ibaa, u_int32_t val);
static u_char IBAA_Byte(struct ibaa_state *ibaa);
static void
IBAA_Init(struct ibaa_state *ibaa)
{
size_t i;
for (i = 0; i < SIZE; ++i) {
ibaa->IBAA_memory[i] = i;
}
ibaa->memIndex = 0;
ibaa->IBAA_aa = 0;
ibaa->IBAA_bb = 0;
ibaa->IBAA_counter = 0;
ibaa->IBAA_byte_index = sizeof(ibaa->IBAA_results);
}
static void
IBAA_Call (struct ibaa_state *ibaa)
{
IBAA(ibaa->IBAA_memory, ibaa->IBAA_results,
&ibaa->IBAA_aa, &ibaa->IBAA_bb,
&ibaa->IBAA_counter);
ibaa->IBAA_byte_index = 0;
}
static void
IBAA_Seed (struct ibaa_state *ibaa, const u_int32_t val)
{
u4 *iptr;
iptr = &ibaa->IBAA_memory[ibaa->memIndex & MASK];
*iptr = ((*iptr << 3) | (*iptr >> 29)) + (val ^ (IBAA_Byte(ibaa) & 15));
++ibaa->memIndex;
}
static void
IBAA_Vector (struct ibaa_state *ibaa, const char *buf, int bytes)
{
int i;
while (bytes >= sizeof(int)) {
IBAA_Seed(ibaa, *(const int *)buf);
buf += sizeof(int);
bytes -= sizeof(int);
}
for (i = 0; i < 10; ++i)
IBAA_Call(ibaa);
}
static u_char
IBAA_Byte(struct ibaa_state *ibaa)
{
u_char result;
int index;
index = ibaa->IBAA_byte_index;
if (index == sizeof(ibaa->IBAA_results)) {
IBAA_Call(ibaa);
index = 0;
}
result = ((u_char *)ibaa->IBAA_results)[index];
ibaa->IBAA_byte_index = index + 1;
return result;
}
typedef unsigned char LByteType;
static void L15_Swap(struct l15_state *l15,const LByteType pos1, const LByteType pos2);
static void L15_InitState(struct l15_state *l15);
static void L15_KSA(struct l15_state *l15,
const LByteType * const key,
const size_t keyLen);
static void L15_Discard(struct l15_state *l15,
const LByteType numCalls);
static void L15_Init(struct l15_state *l15,
const LByteType * const key,
const size_t keyLen);
static LByteType L15_Byte(struct l15_state *l15);
static void L15_Vector(struct l15_state *l15,
const LByteType * const key,
const size_t keyLen);
static __inline void
L15_Swap(struct l15_state *l15, const LByteType pos1, const LByteType pos2)
{
LByteType save1;
save1 = l15->L15_state[pos1];
l15->L15_state[pos1] = l15->L15_state[pos2];
l15->L15_state[pos2] = save1;
}
static void
L15_InitState (struct l15_state *l15)
{
int i;
for (i = 0; i < L15_STATE_SIZE; ++i)
l15->L15_state[i] = i;
}
#define L_SCHEDULE(xx) \
\
for (i = 0; i < L15_STATE_SIZE; ++i) { \
L15_Swap(l15, i, (l15->stateIndex += (l15->L15_state[i] + (xx)))); \
}
static void
L15_KSA (struct l15_state *l15, const LByteType * const key,
const size_t keyLen)
{
size_t i, keyIndex;
for (keyIndex = 0; keyIndex < keyLen; ++keyIndex) {
L_SCHEDULE(key[keyIndex]);
}
L_SCHEDULE(keyLen);
}
static void
L15_Discard(struct l15_state *l15, const LByteType numCalls)
{
LByteType i;
for (i = 0; i < numCalls; ++i) {
(void)L15_Byte(l15);
}
}
static void
L15_Init(struct l15_state *l15, const LByteType *key,
const size_t keyLen)
{
l15->stateIndex = 0;
l15->L15_x = 0;
l15->L15_start_x = 0;
l15->L15_y = L15_STATE_SIZE - 1;
L15_InitState(l15);
L15_KSA(l15, key, keyLen);
L15_Discard(l15, L15_Byte(l15));
}
static LByteType
L15_Byte(struct l15_state *l15)
{
LByteType z;
L15_Swap(l15, l15->L15_state[l15->L15_x], l15->L15_y);
z = (l15->L15_state [l15->L15_x++] + l15->L15_state[l15->L15_y--]);
if (l15->L15_x == l15->L15_start_x) {
--l15->L15_y;
}
return (l15->L15_state[z]);
}
static void
L15_Vector(struct l15_state *l15, const LByteType * const key,
const size_t keyLen)
{
L15_KSA(l15, key, keyLen);
}
static int rand_thread_value;
static void NANOUP_EVENT(struct timespec *last, struct csprng_state *state);
static thread_t rand_td;
static int sysctl_kern_random(SYSCTL_HANDLER_ARGS);
static int rand_mode = 2;
static struct systimer systimer_rand;
static int sysctl_kern_rand_mode(SYSCTL_HANDLER_ARGS);
SYSCTL_PROC(_kern, OID_AUTO, random, CTLFLAG_RD | CTLFLAG_ANYBODY, 0, 0,
sysctl_kern_random, "I", "Acquire random data");
SYSCTL_PROC(_kern, OID_AUTO, rand_mode, CTLTYPE_STRING | CTLFLAG_RW, NULL, 0,
sysctl_kern_rand_mode, "A", "RNG mode (csprng, ibaa or mixed)");
void
rand_initialize(void)
{
struct csprng_state *state;
struct timespec now;
globaldata_t rgd;
char buf[64];
int i;
int n;
if (ncpus == 1) {
csprng_pcpu = &csprng_boot;
} else {
csprng_pcpu = kmalloc(ncpus * sizeof(*csprng_pcpu),
M_NRANDOM, M_WAITOK | M_ZERO);
}
for (n = 0; n < ncpus; ++n) {
state = &csprng_pcpu[n];
rgd = globaldata_find(n);
csprng_init(state);
IBAA_Init(&state->ibaa);
nanouptime(&now);
L15_Init(&state->l15,
(const LByteType *)&now.tv_nsec, sizeof(now.tv_nsec));
for (i = 0; i < (SIZE / 2); ++i) {
nanotime(&now);
state->inject_counter[RAND_SRC_TIMING] = 0;
add_buffer_randomness_state(state,
(const uint8_t *)&now.tv_nsec,
sizeof(now.tv_nsec),
RAND_SRC_TIMING);
nanouptime(&now);
state->inject_counter[RAND_SRC_TIMING] = 0;
add_buffer_randomness_state(state,
(const uint8_t *)&now.tv_nsec,
sizeof(now.tv_nsec),
RAND_SRC_TIMING);
}
state->inject_counter[RAND_SRC_THREAD2] = 0;
add_buffer_randomness_state(state,
(void *)rgd,
sizeof(*rgd),
RAND_SRC_THREAD2);
for (i = 0; i < 10; ++i)
IBAA_Call(&state->ibaa);
state->inject_counter[RAND_SRC_TIMING] = 0;
add_buffer_randomness_state(state, buf, sizeof(buf),
RAND_SRC_TIMING);
read_random(buf, sizeof(buf), 1);
}
}
SYSINIT(rand1, SI_BOOT2_POST_SMP, SI_ORDER_SECOND, rand_initialize, 0);
void
add_keyboard_randomness(u_char scancode)
{
struct csprng_state *state;
state = iterate_csprng_state(1);
if (state) {
spin_lock(&state->spin);
L15_Vector(&state->l15,
(const LByteType *)&scancode, sizeof (scancode));
++state->nrandevents;
++state->nrandseed;
spin_unlock(&state->spin);
add_interrupt_randomness(0);
}
}
void
add_interrupt_randomness(int intr)
{
if (tsc_present) {
rand_thread_value = (rand_thread_value << 4) ^ 1 ^
((int)rdtsc() % 151);
}
++rand_thread_value;
}
static int
add_buffer_randomness_state(struct csprng_state *state,
const char *buf, int bytes, int srcid)
{
uint8_t ic;
if (state) {
spin_lock(&state->spin);
ic = ++state->inject_counter[srcid & 255];
if (ic & 1) {
L15_Vector(&state->l15, (const LByteType *)buf, bytes);
IBAA_Vector(&state->ibaa, buf, bytes);
csprng_add_entropy(state, srcid & RAND_SRC_MASK,
(const uint8_t *)buf, bytes, 0);
} else if (ic & 2) {
L15_Vector(&state->l15, (const LByteType *)buf, bytes);
IBAA_Vector(&state->ibaa, buf, bytes);
} else {
csprng_add_entropy(state, srcid & RAND_SRC_MASK,
(const uint8_t *)buf, bytes, 0);
}
++state->nrandevents;
state->nrandseed += bytes;
spin_unlock(&state->spin);
wakeup(state);
}
return 0;
}
int
add_buffer_randomness(const char *buf, int bytes)
{
struct csprng_state *state;
state = iterate_csprng_state(bytes);
return add_buffer_randomness_state(state, buf, bytes, RAND_SRC_UNKNOWN);
}
int
add_buffer_randomness_src(const char *buf, int bytes, int srcid)
{
struct csprng_state *state;
int n;
while (csprng_pcpu && bytes) {
n = bytes;
if (srcid & RAND_SRCF_PCPU) {
state = &csprng_pcpu[mycpu->gd_cpuid];
} else {
state = iterate_csprng_state(bytes);
if (n > 256)
n = 256;
}
add_buffer_randomness_state(state, buf, bytes, srcid);
bytes -= n;
buf += n;
}
return 0;
}
int
random_filter_read(struct knote *kn, long hint)
{
return (1);
}
u_int
read_random(void *buf, u_int nbytes, int unlimited)
{
struct csprng_state *state;
int i, j;
if (csprng_pcpu == NULL) {
kprintf("read_random: csprng not yet ready\n");
return 0;
}
state = &csprng_pcpu[mycpu->gd_cpuid];
spin_lock(&state->spin);
if (rand_mode == 0) {
i = csprng_get_random(state, buf, nbytes,
unlimited ? CSPRNG_UNLIMITED : 0);
} else if (rand_mode == 1) {
for (i = 0; i < nbytes; i++)
((u_char *)buf)[i] = IBAA_Byte(&state->ibaa);
} else {
i = csprng_get_random(state, buf, nbytes,
unlimited ? CSPRNG_UNLIMITED : 0);
for (j = 0; j < i; j++)
((u_char *)buf)[j] ^= IBAA_Byte(&state->ibaa);
}
spin_unlock(&state->spin);
add_interrupt_randomness(0);
return (i > 0) ? i : 0;
}
static
int
sysctl_kern_random(SYSCTL_HANDLER_ARGS)
{
char buf[256];
size_t n;
size_t r;
int error = 0;
n = req->oldlen;
if (n > 1024 * 1024)
n = 1024 * 1024;
while (n > 0) {
if ((r = n) > sizeof(buf))
r = sizeof(buf);
read_random(buf, r, 1);
error = SYSCTL_OUT(req, buf, r);
if (error)
break;
n -= r;
}
return(error);
}
int
sys_getrandom(struct sysmsg *sysmsg, const struct getrandom_args *uap)
{
char buf[256];
ssize_t bytes;
ssize_t r;
ssize_t n;
int error;
int sigcnt;
bytes = (ssize_t)uap->len;
if (bytes < 0)
return EINVAL;
r = 0;
error = 0;
sigcnt = 0;
while (r < bytes) {
n = (ssize_t)sizeof(buf);
if (n > bytes - r)
n = bytes - r;
read_random(buf, n, 1);
error = copyout(buf, (char *)uap->buf + r, n);
if (error)
break;
r += n;
lwkt_user_yield();
if (++sigcnt == 128) {
sigcnt = 0;
if (CURSIG_NOBLOCK(curthread->td_lwp) != 0) {
error = EINTR;
break;
}
}
}
if (error == 0)
sysmsg->sysmsg_szresult = r;
return error;
}
static
const char *
rand_mode_to_str(int mode)
{
switch (mode) {
case 0:
return "csprng";
case 1:
return "ibaa";
case 2:
return "mixed";
default:
return "unknown";
}
}
static
int
sysctl_kern_rand_mode(SYSCTL_HANDLER_ARGS)
{
char mode[32];
int error;
strncpy(mode, rand_mode_to_str(rand_mode), sizeof(mode)-1);
error = sysctl_handle_string(oidp, mode, sizeof(mode), req);
if (error || req->newptr == NULL)
return error;
if ((strncmp(mode, "csprng", sizeof(mode))) == 0)
rand_mode = 0;
else if ((strncmp(mode, "ibaa", sizeof(mode))) == 0)
rand_mode = 1;
else if ((strncmp(mode, "mixed", sizeof(mode))) == 0)
rand_mode = 2;
else
error = EINVAL;
return error;
}
static
void
rand_thread_loop(void *dummy)
{
struct csprng_state *state;
globaldata_t rgd;
int64_t count;
char buf[32];
uint32_t wcpu;
struct timespec last;
wcpu = 0;
bzero(&last, sizeof(last));
for (;;) {
wcpu = (wcpu + 1) % ncpus;
state = &csprng_pcpu[wcpu];
rgd = globaldata_find(wcpu);
NANOUP_EVENT(&last, state);
spin_lock(&state->spin);
count = (uint8_t)L15_Byte(&state->l15);
spin_unlock(&state->spin);
count = muldivu64(sys_cputimer->freq, count + 256, 256 * 10);
if (time_uptime < 120)
count = count / 10 + 1;
systimer_rand.periodic = count;
add_buffer_randomness_state(state,
buf, sizeof(buf),
RAND_SRC_THREAD1);
add_buffer_randomness_state(state,
(void *)&rgd->gd_cnt,
sizeof(rgd->gd_cnt),
RAND_SRC_THREAD2);
add_buffer_randomness_state(state,
(void *)&rgd->gd_vmtotal,
sizeof(rgd->gd_vmtotal),
RAND_SRC_THREAD3);
read_random(buf, sizeof(buf), 1);
tsleep(rand_td, 0, "rwait", 0);
}
}
static
void
rand_thread_wakeup(struct systimer *timer, int in_ipi, struct intrframe *frame)
{
wakeup(rand_td);
}
static
void
rand_thread_init(void)
{
systimer_init_periodic_nq(&systimer_rand, rand_thread_wakeup, NULL, 25);
lwkt_create(rand_thread_loop, NULL, &rand_td, NULL, 0, 0, "random");
}
SYSINIT(rand2, SI_SUB_HELPER_THREADS, SI_ORDER_ANY, rand_thread_init, 0);
static void
NANOUP_EVENT(struct timespec *last, struct csprng_state *state)
{
struct timespec now;
int nsec;
nanouptime(&now);
nsec = now.tv_nsec - last->tv_nsec;
*last = now;
nsec ^= rand_thread_value;
if (tsc_present)
nsec ^= (rdtsc() & 255) << 8;
add_buffer_randomness_state(state,
(const uint8_t *)&nsec, sizeof(nsec),
RAND_SRC_INTR);
}