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