10Sigor@sysoev.ru
20Sigor@sysoev.ru /*
30Sigor@sysoev.ru * Copyright (C) Igor Sysoev
40Sigor@sysoev.ru * Copyright (C) NGINX, Inc.
50Sigor@sysoev.ru */
60Sigor@sysoev.ru
70Sigor@sysoev.ru
80Sigor@sysoev.ru #include <nxt_main.h>
90Sigor@sysoev.ru
100Sigor@sysoev.ru
110Sigor@sysoev.ru /*
120Sigor@sysoev.ru * The pseudorandom generator based on OpenBSD arc4random. Although it is
130Sigor@sysoev.ru * usually stated that arc4random uses RC4 pseudorandom generation algorithm
140Sigor@sysoev.ru * they are actually different in nxt_random_add().
150Sigor@sysoev.ru */
160Sigor@sysoev.ru
170Sigor@sysoev.ru
180Sigor@sysoev.ru #define NXT_RANDOM_KEY_SIZE 128
190Sigor@sysoev.ru
200Sigor@sysoev.ru
210Sigor@sysoev.ru nxt_inline void nxt_random_start_schedule(nxt_random_t *r);
220Sigor@sysoev.ru static void nxt_random_stir(nxt_random_t *r);
230Sigor@sysoev.ru static void nxt_random_add(nxt_random_t *r, const u_char *key, uint32_t len);
240Sigor@sysoev.ru nxt_inline uint8_t nxt_random_byte(nxt_random_t *r);
250Sigor@sysoev.ru
260Sigor@sysoev.ru
270Sigor@sysoev.ru void
nxt_random_init(nxt_random_t * r)280Sigor@sysoev.ru nxt_random_init(nxt_random_t *r)
290Sigor@sysoev.ru {
300Sigor@sysoev.ru nxt_random_start_schedule(r);
310Sigor@sysoev.ru
320Sigor@sysoev.ru nxt_random_stir(r);
330Sigor@sysoev.ru }
340Sigor@sysoev.ru
350Sigor@sysoev.ru
360Sigor@sysoev.ru nxt_inline void
nxt_random_start_schedule(nxt_random_t * r)370Sigor@sysoev.ru nxt_random_start_schedule(nxt_random_t *r)
380Sigor@sysoev.ru {
390Sigor@sysoev.ru nxt_uint_t i;
400Sigor@sysoev.ru
410Sigor@sysoev.ru r->i = 0;
420Sigor@sysoev.ru r->j = 0;
430Sigor@sysoev.ru
440Sigor@sysoev.ru for (i = 0; i < 256; i++) {
450Sigor@sysoev.ru r->s[i] = i;
460Sigor@sysoev.ru }
470Sigor@sysoev.ru }
480Sigor@sysoev.ru
490Sigor@sysoev.ru
500Sigor@sysoev.ru static void
nxt_random_stir(nxt_random_t * r)510Sigor@sysoev.ru nxt_random_stir(nxt_random_t *r)
520Sigor@sysoev.ru {
530Sigor@sysoev.ru int fd;
540Sigor@sysoev.ru ssize_t n;
550Sigor@sysoev.ru struct timeval tv;
560Sigor@sysoev.ru union {
57138Sigor@sysoev.ru uint32_t value[4];
580Sigor@sysoev.ru u_char bytes[NXT_RANDOM_KEY_SIZE];
590Sigor@sysoev.ru } key;
600Sigor@sysoev.ru
610Sigor@sysoev.ru #if (NXT_HAVE_GETRANDOM)
620Sigor@sysoev.ru
63676Spluknet@nginx.com n = getrandom(&key, NXT_RANDOM_KEY_SIZE, 0);
64676Spluknet@nginx.com
65676Spluknet@nginx.com #elif (NXT_HAVE_LINUX_SYS_GETRANDOM)
660Sigor@sysoev.ru
67676Spluknet@nginx.com /* Linux 3.17 SYS_getrandom. */
68676Spluknet@nginx.com
69676Spluknet@nginx.com n = syscall(SYS_getrandom, &key, NXT_RANDOM_KEY_SIZE, 0);
700Sigor@sysoev.ru
71733Spluknet@nginx.com #elif (NXT_HAVE_GETENTROPY || NXT_HAVE_GETENTROPY_SYS_RANDOM)
72733Spluknet@nginx.com
73733Spluknet@nginx.com n = 0;
74733Spluknet@nginx.com
75733Spluknet@nginx.com if (getentropy(&key, NXT_RANDOM_KEY_SIZE) == 0) {
76733Spluknet@nginx.com n = NXT_RANDOM_KEY_SIZE;
77733Spluknet@nginx.com }
78733Spluknet@nginx.com
79677Spluknet@nginx.com #else
80677Spluknet@nginx.com
81677Spluknet@nginx.com n = 0;
82677Spluknet@nginx.com
830Sigor@sysoev.ru #endif
840Sigor@sysoev.ru
850Sigor@sysoev.ru if (n != NXT_RANDOM_KEY_SIZE) {
860Sigor@sysoev.ru fd = open("/dev/urandom", O_RDONLY);
870Sigor@sysoev.ru
880Sigor@sysoev.ru if (fd >= 0) {
890Sigor@sysoev.ru n = read(fd, &key, NXT_RANDOM_KEY_SIZE);
900Sigor@sysoev.ru (void) close(fd);
910Sigor@sysoev.ru }
920Sigor@sysoev.ru }
930Sigor@sysoev.ru
940Sigor@sysoev.ru if (n != NXT_RANDOM_KEY_SIZE) {
950Sigor@sysoev.ru (void) gettimeofday(&tv, NULL);
960Sigor@sysoev.ru
970Sigor@sysoev.ru /* XOR with stack garbage. */
980Sigor@sysoev.ru
990Sigor@sysoev.ru key.value[0] ^= tv.tv_usec;
1000Sigor@sysoev.ru key.value[1] ^= tv.tv_sec;
1010Sigor@sysoev.ru key.value[2] ^= nxt_pid;
102*737Svbart@nginx.com key.value[3] ^= (uintptr_t) nxt_thread_tid(nxt_thread());
1030Sigor@sysoev.ru }
1040Sigor@sysoev.ru
1050Sigor@sysoev.ru nxt_random_add(r, key.bytes, NXT_RANDOM_KEY_SIZE);
1060Sigor@sysoev.ru
1070Sigor@sysoev.ru /* Drop the first 3072 bytes. */
1080Sigor@sysoev.ru for (n = 3072; n != 0; n--) {
1090Sigor@sysoev.ru (void) nxt_random_byte(r);
1100Sigor@sysoev.ru }
1110Sigor@sysoev.ru
1120Sigor@sysoev.ru /* Stir again after 1,600,000 bytes. */
1130Sigor@sysoev.ru r->count = 400000;
1140Sigor@sysoev.ru }
1150Sigor@sysoev.ru
1160Sigor@sysoev.ru
1170Sigor@sysoev.ru static void
nxt_random_add(nxt_random_t * r,const u_char * key,uint32_t len)1180Sigor@sysoev.ru nxt_random_add(nxt_random_t *r, const u_char *key, uint32_t len)
1190Sigor@sysoev.ru {
1200Sigor@sysoev.ru uint8_t val;
1210Sigor@sysoev.ru uint32_t n;
1220Sigor@sysoev.ru
1230Sigor@sysoev.ru for (n = 0; n < 256; n++) {
1240Sigor@sysoev.ru val = r->s[r->i];
1250Sigor@sysoev.ru r->j += val + key[n % len];
1260Sigor@sysoev.ru
1270Sigor@sysoev.ru r->s[r->i] = r->s[r->j];
1280Sigor@sysoev.ru r->s[r->j] = val;
1290Sigor@sysoev.ru
1300Sigor@sysoev.ru r->i++;
1310Sigor@sysoev.ru }
1320Sigor@sysoev.ru
1330Sigor@sysoev.ru /* This index is not decremented in RC4 algorithm. */
1340Sigor@sysoev.ru r->i--;
1350Sigor@sysoev.ru
1360Sigor@sysoev.ru r->j = r->i;
1370Sigor@sysoev.ru }
1380Sigor@sysoev.ru
1390Sigor@sysoev.ru
1400Sigor@sysoev.ru uint32_t
nxt_random(nxt_random_t * r)1410Sigor@sysoev.ru nxt_random(nxt_random_t *r)
1420Sigor@sysoev.ru {
1430Sigor@sysoev.ru uint32_t val;
1440Sigor@sysoev.ru
1450Sigor@sysoev.ru r->count--;
1460Sigor@sysoev.ru
1470Sigor@sysoev.ru if (r->count <= 0) {
1480Sigor@sysoev.ru nxt_random_stir(r);
1490Sigor@sysoev.ru }
1500Sigor@sysoev.ru
1510Sigor@sysoev.ru val = nxt_random_byte(r) << 24;
1520Sigor@sysoev.ru val |= nxt_random_byte(r) << 16;
1530Sigor@sysoev.ru val |= nxt_random_byte(r) << 8;
1540Sigor@sysoev.ru val |= nxt_random_byte(r);
1550Sigor@sysoev.ru
1560Sigor@sysoev.ru return val;
1570Sigor@sysoev.ru }
1580Sigor@sysoev.ru
1590Sigor@sysoev.ru
1600Sigor@sysoev.ru nxt_inline uint8_t
nxt_random_byte(nxt_random_t * r)1610Sigor@sysoev.ru nxt_random_byte(nxt_random_t *r)
1620Sigor@sysoev.ru {
1630Sigor@sysoev.ru uint8_t si, sj;
1640Sigor@sysoev.ru
1650Sigor@sysoev.ru r->i++;
1660Sigor@sysoev.ru si = r->s[r->i];
1670Sigor@sysoev.ru r->j += si;
1680Sigor@sysoev.ru
1690Sigor@sysoev.ru sj = r->s[r->j];
1700Sigor@sysoev.ru r->s[r->i] = sj;
1710Sigor@sysoev.ru r->s[r->j] = si;
1720Sigor@sysoev.ru
1730Sigor@sysoev.ru si += sj;
1740Sigor@sysoev.ru
1750Sigor@sysoev.ru return r->s[si];
1760Sigor@sysoev.ru }
1770Sigor@sysoev.ru
1780Sigor@sysoev.ru
179246Sigor@sysoev.ru #if (NXT_TESTS)
1800Sigor@sysoev.ru
1810Sigor@sysoev.ru nxt_int_t
nxt_random_test(nxt_thread_t * thr)182246Sigor@sysoev.ru nxt_random_test(nxt_thread_t *thr)
1830Sigor@sysoev.ru {
1840Sigor@sysoev.ru nxt_uint_t n;
1850Sigor@sysoev.ru nxt_random_t r;
1860Sigor@sysoev.ru
1870Sigor@sysoev.ru nxt_random_start_schedule(&r);
1880Sigor@sysoev.ru
1890Sigor@sysoev.ru r.count = 400000;
1900Sigor@sysoev.ru
191703Svbart@nginx.com nxt_random_add(&r, (u_char *) "arc4random", nxt_length("arc4random"));
1920Sigor@sysoev.ru
1930Sigor@sysoev.ru /*
1940Sigor@sysoev.ru * Test arc4random() numbers.
1950Sigor@sysoev.ru * RC4 pseudorandom numbers would be 0x4642AFC3 and 0xBAF0FFF0.
1960Sigor@sysoev.ru */
1970Sigor@sysoev.ru
1980Sigor@sysoev.ru if (nxt_random(&r) == 0xD6270B27) {
1990Sigor@sysoev.ru
2000Sigor@sysoev.ru for (n = 100000; n != 0; n--) {
2010Sigor@sysoev.ru (void) nxt_random(&r);
2020Sigor@sysoev.ru }
2030Sigor@sysoev.ru
2040Sigor@sysoev.ru if (nxt_random(&r) == 0x6FCAE186) {
205246Sigor@sysoev.ru nxt_log_error(NXT_LOG_NOTICE, thr->log, "arc4random test passed");
2060Sigor@sysoev.ru
2070Sigor@sysoev.ru return NXT_OK;
2080Sigor@sysoev.ru }
2090Sigor@sysoev.ru }
2100Sigor@sysoev.ru
211246Sigor@sysoev.ru nxt_log_error(NXT_LOG_NOTICE, thr->log, "arc4random test failed");
2120Sigor@sysoev.ru
2130Sigor@sysoev.ru return NXT_ERROR;
2140Sigor@sysoev.ru }
2150Sigor@sysoev.ru
2160Sigor@sysoev.ru #endif
217