/* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #include /* * The pseudorandom generator based on OpenBSD arc4random. Although it is * usually stated that arc4random uses RC4 pseudorandom generation algorithm * they are actually different in nxt_random_add(). */ #define NXT_RANDOM_KEY_SIZE 128 nxt_inline void nxt_random_start_schedule(nxt_random_t *r); static void nxt_random_stir(nxt_random_t *r); static void nxt_random_add(nxt_random_t *r, const u_char *key, uint32_t len); nxt_inline uint8_t nxt_random_byte(nxt_random_t *r); void nxt_random_init(nxt_random_t *r) { nxt_random_start_schedule(r); nxt_random_stir(r); } nxt_inline void nxt_random_start_schedule(nxt_random_t *r) { nxt_uint_t i; r->i = 0; r->j = 0; for (i = 0; i < 256; i++) { r->s[i] = i; } } static void nxt_random_stir(nxt_random_t *r) { int fd; ssize_t n; struct timeval tv; union { uint32_t value[4]; u_char bytes[NXT_RANDOM_KEY_SIZE]; } key; #if (NXT_HAVE_GETRANDOM) n = getrandom(&key, NXT_RANDOM_KEY_SIZE, 0); #elif (NXT_HAVE_LINUX_SYS_GETRANDOM) /* Linux 3.17 SYS_getrandom. */ n = syscall(SYS_getrandom, &key, NXT_RANDOM_KEY_SIZE, 0); #elif (NXT_HAVE_GETENTROPY || NXT_HAVE_GETENTROPY_SYS_RANDOM) n = 0; if (getentropy(&key, NXT_RANDOM_KEY_SIZE) == 0) { n = NXT_RANDOM_KEY_SIZE; } #else n = 0; #endif if (n != NXT_RANDOM_KEY_SIZE) { fd = open("/dev/urandom", O_RDONLY); if (fd >= 0) { n = read(fd, &key, NXT_RANDOM_KEY_SIZE); (void) close(fd); } } if (n != NXT_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] ^= nxt_pid; key.value[3] ^= (uintptr_t) nxt_thread_tid(nxt_thread()); } nxt_random_add(r, key.bytes, NXT_RANDOM_KEY_SIZE); /* Drop the first 3072 bytes. */ for (n = 3072; n != 0; n--) { (void) nxt_random_byte(r); } /* Stir again after 1,600,000 bytes. */ r->count = 400000; } static void nxt_random_add(nxt_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 nxt_random(nxt_random_t *r) { uint32_t val; r->count--; if (r->count <= 0) { nxt_random_stir(r); } val = nxt_random_byte(r) << 24; val |= nxt_random_byte(r) << 16; val |= nxt_random_byte(r) << 8; val |= nxt_random_byte(r); return val; } nxt_inline uint8_t nxt_random_byte(nxt_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]; } #if (NXT_TESTS) nxt_int_t nxt_random_test(nxt_thread_t *thr) { nxt_uint_t n; nxt_random_t r; nxt_random_start_schedule(&r); r.count = 400000; nxt_random_add(&r, (u_char *) "arc4random", nxt_length("arc4random")); /* * Test arc4random() numbers. * RC4 pseudorandom numbers would be 0x4642AFC3 and 0xBAF0FFF0. */ if (nxt_random(&r) == 0xD6270B27) { for (n = 100000; n != 0; n--) { (void) nxt_random(&r); } if (nxt_random(&r) == 0x6FCAE186) { nxt_log_error(NXT_LOG_NOTICE, thr->log, "arc4random test passed"); return NXT_OK; } } nxt_log_error(NXT_LOG_NOTICE, thr->log, "arc4random test failed"); return NXT_ERROR; } #endif