xref: /unit/src/nxt_random.c (revision 138:59fc46dd5e1d)
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