xref: /unit/src/nxt_random.c (revision 737:8ca436f03869)
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