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