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 #if (NXT_HAVE_GETRANDOM) 62 63 n = getrandom(&key, NXT_RANDOM_KEY_SIZE, 0); 64 65 #elif (NXT_HAVE_LINUX_SYS_GETRANDOM) 66 67 /* Linux 3.17 SYS_getrandom. */ 68 69 n = syscall(SYS_getrandom, &key, NXT_RANDOM_KEY_SIZE, 0); 70 71 #else 72 73 n = 0; 74 75 #endif 76 77 if (n != NXT_RANDOM_KEY_SIZE) { 78 fd = open("/dev/urandom", O_RDONLY); 79 80 if (fd >= 0) { 81 n = read(fd, &key, NXT_RANDOM_KEY_SIZE); 82 (void) close(fd); 83 } 84 } 85 86 if (n != NXT_RANDOM_KEY_SIZE) { 87 (void) gettimeofday(&tv, NULL); 88 89 /* XOR with stack garbage. */ 90 91 key.value[0] ^= tv.tv_usec; 92 key.value[1] ^= tv.tv_sec; 93 key.value[2] ^= nxt_pid; 94 key.value[3] ^= (uintptr_t) nxt_thread_tid(NULL); 95 } 96 97 nxt_random_add(r, key.bytes, NXT_RANDOM_KEY_SIZE); 98 99 /* Drop the first 3072 bytes. */ 100 for (n = 3072; n != 0; n--) { 101 (void) nxt_random_byte(r); 102 } 103 104 /* Stir again after 1,600,000 bytes. */ 105 r->count = 400000; 106 } 107 108 109 static void 110 nxt_random_add(nxt_random_t *r, const u_char *key, uint32_t len) 111 { 112 uint8_t val; 113 uint32_t n; 114 115 for (n = 0; n < 256; n++) { 116 val = r->s[r->i]; 117 r->j += val + key[n % len]; 118 119 r->s[r->i] = r->s[r->j]; 120 r->s[r->j] = val; 121 122 r->i++; 123 } 124 125 /* This index is not decremented in RC4 algorithm. */ 126 r->i--; 127 128 r->j = r->i; 129 } 130 131 132 uint32_t 133 nxt_random(nxt_random_t *r) 134 { 135 uint32_t val; 136 137 r->count--; 138 139 if (r->count <= 0) { 140 nxt_random_stir(r); 141 } 142 143 val = nxt_random_byte(r) << 24; 144 val |= nxt_random_byte(r) << 16; 145 val |= nxt_random_byte(r) << 8; 146 val |= nxt_random_byte(r); 147 148 return val; 149 } 150 151 152 nxt_inline uint8_t 153 nxt_random_byte(nxt_random_t *r) 154 { 155 uint8_t si, sj; 156 157 r->i++; 158 si = r->s[r->i]; 159 r->j += si; 160 161 sj = r->s[r->j]; 162 r->s[r->i] = sj; 163 r->s[r->j] = si; 164 165 si += sj; 166 167 return r->s[si]; 168 } 169 170 171 #if (NXT_TESTS) 172 173 nxt_int_t 174 nxt_random_test(nxt_thread_t *thr) 175 { 176 nxt_uint_t n; 177 nxt_random_t r; 178 179 nxt_random_start_schedule(&r); 180 181 r.count = 400000; 182 183 nxt_random_add(&r, (u_char *) "arc4random", sizeof("arc4random") - 1); 184 185 /* 186 * Test arc4random() numbers. 187 * RC4 pseudorandom numbers would be 0x4642AFC3 and 0xBAF0FFF0. 188 */ 189 190 if (nxt_random(&r) == 0xD6270B27) { 191 192 for (n = 100000; n != 0; n--) { 193 (void) nxt_random(&r); 194 } 195 196 if (nxt_random(&r) == 0x6FCAE186) { 197 nxt_log_error(NXT_LOG_NOTICE, thr->log, "arc4random test passed"); 198 199 return NXT_OK; 200 } 201 } 202 203 nxt_log_error(NXT_LOG_NOTICE, thr->log, "arc4random test failed"); 204 205 return NXT_ERROR; 206 } 207 208 #endif 209