1 2 /* 3 * Copyright (C) Igor Sysoev 4 * Copyright (C) NGINX, Inc. 5 */ 6 7 8 #include <nxt_main.h> 9 10 11 /* 12 * The pseudorandom generator based on OpenBSD arc4random. Although it is 13 * usually stated that arc4random uses RC4 pseudorandom generation algorithm 14 * they are actually different in nxt_random_add(). 15 */ 16 17 18 #define NXT_RANDOM_KEY_SIZE 128 19 20 21 nxt_inline void nxt_random_start_schedule(nxt_random_t *r); 22 static void nxt_random_stir(nxt_random_t *r); 23 static void nxt_random_add(nxt_random_t *r, const u_char *key, uint32_t len); 24 nxt_inline uint8_t nxt_random_byte(nxt_random_t *r); 25 26 27 void 28 nxt_random_init(nxt_random_t *r) 29 { 30 nxt_random_start_schedule(r); 31 32 nxt_random_stir(r); 33 } 34 35 36 nxt_inline void 37 nxt_random_start_schedule(nxt_random_t *r) 38 { 39 nxt_uint_t i; 40 41 r->i = 0; 42 r->j = 0; 43 44 for (i = 0; i < 256; i++) { 45 r->s[i] = i; 46 } 47 } 48 49 50 static void 51 nxt_random_stir(nxt_random_t *r) 52 { 53 int fd; 54 ssize_t n; 55 struct timeval tv; 56 union { 57 uint32_t value[4]; 58 u_char bytes[NXT_RANDOM_KEY_SIZE]; 59 } key; 60 61 n = 0; 62 63 #if (NXT_HAVE_GETRANDOM) 64 65 /* Linux 3.17 getrandom(). */ 66 67 n = getrandom(key, NXT_RANDOM_KEY_SIZE, 0); 68 69 #endif 70 71 if (n != NXT_RANDOM_KEY_SIZE) { 72 fd = open("/dev/urandom", O_RDONLY); 73 74 if (fd >= 0) { 75 n = read(fd, &key, NXT_RANDOM_KEY_SIZE); 76 (void) close(fd); 77 } 78 } 79 80 if (n != NXT_RANDOM_KEY_SIZE) { 81 (void) gettimeofday(&tv, NULL); 82 83 /* XOR with stack garbage. */ 84 85 key.value[0] ^= tv.tv_usec; 86 key.value[1] ^= tv.tv_sec; 87 key.value[2] ^= nxt_pid; 88 key.value[3] ^= nxt_thread_tid(NULL); 89 } 90 91 nxt_random_add(r, key.bytes, NXT_RANDOM_KEY_SIZE); 92 93 /* Drop the first 3072 bytes. */ 94 for (n = 3072; n != 0; n--) { 95 (void) nxt_random_byte(r); 96 } 97 98 /* Stir again after 1,600,000 bytes. */ 99 r->count = 400000; 100 } 101 102 103 static void 104 nxt_random_add(nxt_random_t *r, const u_char *key, uint32_t len) 105 { 106 uint8_t val; 107 uint32_t n; 108 109 for (n = 0; n < 256; n++) { 110 val = r->s[r->i]; 111 r->j += val + key[n % len]; 112 113 r->s[r->i] = r->s[r->j]; 114 r->s[r->j] = val; 115 116 r->i++; 117 } 118 119 /* This index is not decremented in RC4 algorithm. */ 120 r->i--; 121 122 r->j = r->i; 123 } 124 125 126 uint32_t 127 nxt_random(nxt_random_t *r) 128 { 129 uint32_t val; 130 131 r->count--; 132 133 if (r->count <= 0) { 134 nxt_random_stir(r); 135 } 136 137 val = nxt_random_byte(r) << 24; 138 val |= nxt_random_byte(r) << 16; 139 val |= nxt_random_byte(r) << 8; 140 val |= nxt_random_byte(r); 141 142 return val; 143 } 144 145 146 nxt_inline uint8_t 147 nxt_random_byte(nxt_random_t *r) 148 { 149 uint8_t si, sj; 150 151 r->i++; 152 si = r->s[r->i]; 153 r->j += si; 154 155 sj = r->s[r->j]; 156 r->s[r->i] = sj; 157 r->s[r->j] = si; 158 159 si += sj; 160 161 return r->s[si]; 162 } 163 164 165 #if (NXT_LIB_UNIT_TEST) 166 167 nxt_int_t 168 nxt_random_unit_test(nxt_thread_t *thr) 169 { 170 nxt_uint_t n; 171 nxt_random_t r; 172 173 nxt_random_start_schedule(&r); 174 175 r.count = 400000; 176 177 nxt_random_add(&r, (u_char *) "arc4random", sizeof("arc4random") - 1); 178 179 /* 180 * Test arc4random() numbers. 181 * RC4 pseudorandom numbers would be 0x4642AFC3 and 0xBAF0FFF0. 182 */ 183 184 if (nxt_random(&r) == 0xD6270B27) { 185 186 for (n = 100000; n != 0; n--) { 187 (void) nxt_random(&r); 188 } 189 190 if (nxt_random(&r) == 0x6FCAE186) { 191 nxt_log_error(NXT_LOG_NOTICE, thr->log, 192 "arc4random unit test passed"); 193 194 return NXT_OK; 195 } 196 } 197 198 nxt_log_error(NXT_LOG_NOTICE, thr->log, "arc4random unit test failed"); 199 200 return NXT_ERROR; 201 } 202 203 #endif 204