blob: 806b4d166f48400752c8fbcd44ce3e4d72e0144f [file] [log] [blame]
/*
* Copyright (C) Igor Sysoev
* Copyright (C) NGINX, Inc.
*/
#include <njs_main.h>
#if (NJS_HAVE_GETRANDOM)
#include <sys/random.h>
#elif (NJS_HAVE_LINUX_SYS_GETRANDOM)
#include <sys/syscall.h>
#include <linux/random.h>
#elif (NJS_HAVE_GETENTROPY_SYS_RANDOM)
#include <sys/random.h>
#endif
/*
* The pseudorandom generator based on OpenBSD arc4random. Although
* it is usually stated that arc4random uses RC4 pseudorandom generation
* algorithm they are actually different in njs_random_add().
*/
#define NJS_RANDOM_KEY_SIZE 128
njs_inline uint8_t njs_random_byte(njs_random_t *r);
void
njs_random_init(njs_random_t *r, njs_pid_t pid)
{
njs_uint_t i;
r->count = 0;
r->pid = pid;
r->i = 0;
r->j = 0;
for (i = 0; i < 256; i++) {
r->s[i] = i;
}
}
void
njs_random_stir(njs_random_t *r, njs_pid_t pid)
{
int fd;
ssize_t n;
struct timeval tv;
union {
uint32_t value[3];
u_char bytes[NJS_RANDOM_KEY_SIZE];
} key;
if (r->pid == 0) {
njs_random_init(r, pid);
}
r->pid = pid;
#if (NJS_HAVE_GETRANDOM)
n = getrandom(&key, NJS_RANDOM_KEY_SIZE, 0);
#elif (NJS_HAVE_LINUX_SYS_GETRANDOM)
/* Linux 3.17 SYS_getrandom, not available in Glibc prior to 2.25. */
n = syscall(SYS_getrandom, &key, NJS_RANDOM_KEY_SIZE, 0);
#elif (NJS_HAVE_GETENTROPY || NJS_HAVE_GETENTROPY_SYS_RANDOM)
n = 0;
if (getentropy(&key, NJS_RANDOM_KEY_SIZE) == 0) {
n = NJS_RANDOM_KEY_SIZE;
}
#else
n = 0;
#endif
if (n != NJS_RANDOM_KEY_SIZE) {
fd = open("/dev/urandom", O_RDONLY);
if (fd >= 0) {
n = read(fd, &key, NJS_RANDOM_KEY_SIZE);
(void) close(fd);
}
}
if (n != NJS_RANDOM_KEY_SIZE) {
(void) gettimeofday(&tv, NULL);
/* XOR with stack garbage. */
key.value[0] ^= tv.tv_usec;
key.value[1] ^= tv.tv_sec;
key.value[2] ^= getpid();
}
njs_msan_unpoison(&key, NJS_RANDOM_KEY_SIZE);
njs_random_add(r, key.bytes, NJS_RANDOM_KEY_SIZE);
/* Drop the first 3072 bytes. */
for (n = 3072; n != 0; n--) {
(void) njs_random_byte(r);
}
/* Stir again after 1,600,000 bytes. */
r->count = 400000;
}
void
njs_random_add(njs_random_t *r, const u_char *key, uint32_t len)
{
uint8_t val;
uint32_t n;
for (n = 0; n < 256; n++) {
val = r->s[r->i];
r->j += val + key[n % len];
r->s[r->i] = r->s[r->j];
r->s[r->j] = val;
r->i++;
}
/* This index is not decremented in RC4 algorithm. */
r->i--;
r->j = r->i;
}
uint32_t
njs_random(njs_random_t *r)
{
uint32_t val;
njs_pid_t pid;
njs_bool_t new_pid;
new_pid = 0;
pid = r->pid;
if (pid != -1) {
pid = getpid();
if (pid != r->pid) {
new_pid = 1;
}
}
r->count--;
if (r->count <= 0 || new_pid) {
njs_random_stir(r, pid);
}
val = (uint32_t) njs_random_byte(r) << 24;
val |= (uint32_t) njs_random_byte(r) << 16;
val |= (uint32_t) njs_random_byte(r) << 8;
val |= (uint32_t) njs_random_byte(r);
return val;
}
njs_inline uint8_t
njs_random_byte(njs_random_t *r)
{
uint8_t si, sj;
r->i++;
si = r->s[r->i];
r->j += si;
sj = r->s[r->j];
r->s[r->i] = sj;
r->s[r->j] = si;
si += sj;
return r->s[si];
}