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 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 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 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 71*733Spluknet@nginx.com #elif (NXT_HAVE_GETENTROPY || NXT_HAVE_GETENTROPY_SYS_RANDOM) 72*733Spluknet@nginx.com 73*733Spluknet@nginx.com n = 0; 74*733Spluknet@nginx.com 75*733Spluknet@nginx.com if (getentropy(&key, NXT_RANDOM_KEY_SIZE) == 0) { 76*733Spluknet@nginx.com n = NXT_RANDOM_KEY_SIZE; 77*733Spluknet@nginx.com } 78*733Spluknet@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; 102336Spluknet@nginx.com key.value[3] ^= (uintptr_t) nxt_thread_tid(NULL); 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 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 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 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 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