/src/dropbear/src/dbrandom.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Dropbear - a SSH2 server |
3 | | * |
4 | | * Copyright (c) 2002,2003 Matt Johnston |
5 | | * All rights reserved. |
6 | | * |
7 | | * Permission is hereby granted, free of charge, to any person obtaining a copy |
8 | | * of this software and associated documentation files (the "Software"), to deal |
9 | | * in the Software without restriction, including without limitation the rights |
10 | | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
11 | | * copies of the Software, and to permit persons to whom the Software is |
12 | | * furnished to do so, subject to the following conditions: |
13 | | * |
14 | | * The above copyright notice and this permission notice shall be included in |
15 | | * all copies or substantial portions of the Software. |
16 | | * |
17 | | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
18 | | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
19 | | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
20 | | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
21 | | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
22 | | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
23 | | * SOFTWARE. */ |
24 | | |
25 | | #include "includes.h" |
26 | | #include "buffer.h" |
27 | | #include "dbutil.h" |
28 | | #include "bignum.h" |
29 | | #include "dbrandom.h" |
30 | | #include "runopts.h" |
31 | | |
32 | | /* this is used to generate unique output from the same hashpool */ |
33 | | static uint32_t counter = 0; |
34 | | /* the max value for the counter, so it won't integer overflow */ |
35 | 61.5k | #define MAX_COUNTER (1<<30) |
36 | | |
37 | | static unsigned char hashpool[SHA256_HASH_SIZE] = {0}; |
38 | | static int donerandinit = 0; |
39 | | |
40 | 0 | #define INIT_SEED_SIZE 32 /* 256 bits */ |
41 | | |
42 | | /* The basic setup is we read some data from /dev/(u)random or prngd and hash it |
43 | | * into hashpool. To read data, we hash together current hashpool contents, |
44 | | * and a counter. We feed more data in by hashing the current pool and new |
45 | | * data into the pool. |
46 | | * |
47 | | * It is important to ensure that counter doesn't wrap around before we |
48 | | * feed in new entropy. |
49 | | * |
50 | | */ |
51 | | |
52 | | /* Pass wantlen=0 to hash an entire file */ |
53 | | static int |
54 | | process_file(hash_state *hs, const char *filename, |
55 | 0 | unsigned int wantlen, int prngd) { |
56 | 0 | int readfd = -1; |
57 | 0 | unsigned int readcount; |
58 | 0 | int ret = DROPBEAR_FAILURE; |
59 | |
|
60 | 0 | if (prngd) { |
61 | | #if DROPBEAR_USE_PRNGD |
62 | | readfd = connect_unix(filename); |
63 | | #endif |
64 | 0 | } else { |
65 | 0 | readfd = open(filename, O_RDONLY); |
66 | 0 | } |
67 | |
|
68 | 0 | if (readfd < 0) { |
69 | 0 | goto out; |
70 | 0 | } |
71 | | |
72 | 0 | readcount = 0; |
73 | 0 | while (wantlen == 0 || readcount < wantlen) { |
74 | 0 | int readlen, wantread; |
75 | 0 | unsigned char readbuf[4096]; |
76 | 0 | if (wantlen == 0) { |
77 | 0 | wantread = sizeof(readbuf); |
78 | 0 | } else { |
79 | 0 | wantread = MIN(sizeof(readbuf), wantlen-readcount); |
80 | 0 | } |
81 | |
|
82 | | #if DROPBEAR_USE_PRNGD |
83 | | if (prngd) { |
84 | | char egdcmd[2]; |
85 | | egdcmd[0] = 0x02; /* blocking read */ |
86 | | egdcmd[1] = (unsigned char)wantread; |
87 | | if (write(readfd, egdcmd, 2) < 0) { |
88 | | dropbear_exit("Can't send command to egd"); |
89 | | } |
90 | | } |
91 | | #endif |
92 | 0 | readlen = read(readfd, readbuf, wantread); |
93 | 0 | if (readlen <= 0) { |
94 | 0 | if (readlen < 0 && errno == EINTR) { |
95 | 0 | continue; |
96 | 0 | } |
97 | 0 | if (readlen == 0 && wantlen == 0) { |
98 | | /* whole file was read as requested */ |
99 | 0 | break; |
100 | 0 | } |
101 | 0 | goto out; |
102 | 0 | } |
103 | 0 | sha256_process(hs, readbuf, readlen); |
104 | 0 | readcount += readlen; |
105 | 0 | } |
106 | 0 | ret = DROPBEAR_SUCCESS; |
107 | 0 | out: |
108 | 0 | close(readfd); |
109 | 0 | return ret; |
110 | 0 | } |
111 | | |
112 | | void addrandom(const unsigned char * buf, unsigned int len) |
113 | 0 | { |
114 | 0 | hash_state hs; |
115 | |
|
116 | 0 | #if DROPBEAR_FUZZ |
117 | 0 | if (fuzz.fuzzing) { |
118 | 0 | return; |
119 | 0 | } |
120 | 0 | #endif |
121 | | |
122 | | /* hash in the new seed data */ |
123 | 0 | sha256_init(&hs); |
124 | | /* existing state (zeroes on startup) */ |
125 | 0 | sha256_process(&hs, (void*)hashpool, sizeof(hashpool)); |
126 | | |
127 | | /* new */ |
128 | 0 | sha256_process(&hs, buf, len); |
129 | 0 | sha256_done(&hs, hashpool); |
130 | 0 | } |
131 | | |
132 | | static void write_urandom() |
133 | 0 | { |
134 | 0 | #if DROPBEAR_FUZZ |
135 | 0 | if (fuzz.fuzzing) { |
136 | 0 | return; |
137 | 0 | } |
138 | 0 | #endif |
139 | 0 | #if !DROPBEAR_USE_PRNGD |
140 | | /* This is opportunistic, don't worry about failure */ |
141 | 0 | unsigned char buf[INIT_SEED_SIZE]; |
142 | 0 | FILE *f = fopen(DROPBEAR_URANDOM_DEV, "w"); |
143 | 0 | if (!f) { |
144 | 0 | return; |
145 | 0 | } |
146 | 0 | genrandom(buf, sizeof(buf)); |
147 | 0 | fwrite(buf, sizeof(buf), 1, f); |
148 | 0 | fclose(f); |
149 | 0 | #endif |
150 | 0 | } |
151 | | |
152 | | #if DROPBEAR_FUZZ |
153 | 2.99k | void fuzz_seed(const unsigned char* dat, unsigned int len) { |
154 | 2.99k | hash_state hs; |
155 | 2.99k | sha256_init(&hs); |
156 | 2.99k | sha256_process(&hs, "fuzzfuzzfuzz", strlen("fuzzfuzzfuzz")); |
157 | 2.99k | sha256_process(&hs, dat, len); |
158 | 2.99k | sha256_done(&hs, hashpool); |
159 | 2.99k | counter = 0; |
160 | 2.99k | donerandinit = 1; |
161 | 2.99k | } |
162 | | #endif |
163 | | |
164 | | |
165 | | #ifdef HAVE_GETRANDOM |
166 | | /* Reads entropy seed with getrandom(). |
167 | | * May block if the kernel isn't ready. |
168 | | * Return DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ |
169 | 0 | static int process_getrandom(hash_state *hs) { |
170 | 0 | char buf[INIT_SEED_SIZE]; |
171 | 0 | ssize_t ret; |
172 | | |
173 | | /* First try non-blocking so that we can warn about waiting */ |
174 | 0 | ret = getrandom(buf, sizeof(buf), GRND_NONBLOCK); |
175 | 0 | if (ret == -1) { |
176 | 0 | if (errno == ENOSYS) { |
177 | | /* Old kernel */ |
178 | 0 | return DROPBEAR_FAILURE; |
179 | 0 | } |
180 | | /* Other errors fall through to blocking getrandom() */ |
181 | 0 | TRACE(("first getrandom() failed: %d %s", errno, strerror(errno))) |
182 | 0 | if (errno == EAGAIN) { |
183 | 0 | dropbear_log(LOG_WARNING, "Waiting for kernel randomness to be initialised..."); |
184 | 0 | } |
185 | 0 | } |
186 | | |
187 | | /* Wait blocking if needed. Loop in case we get EINTR */ |
188 | 0 | while (ret != sizeof(buf)) { |
189 | 0 | ret = getrandom(buf, sizeof(buf), 0); |
190 | |
|
191 | 0 | if (ret == sizeof(buf)) { |
192 | | /* Success */ |
193 | 0 | break; |
194 | 0 | } |
195 | 0 | if (ret == -1 && errno == EINTR) { |
196 | | /* Try again. */ |
197 | 0 | continue; |
198 | 0 | } |
199 | 0 | if (ret >= 0) { |
200 | 0 | TRACE(("Short read %zd from getrandom() shouldn't happen", ret)) |
201 | | /* Try again? */ |
202 | 0 | continue; |
203 | 0 | } |
204 | | |
205 | | /* Unexpected problem, fall back to /dev/urandom */ |
206 | 0 | TRACE(("2nd getrandom() failed: %d %s", errno, strerror(errno))) |
207 | 0 | break; |
208 | 0 | } |
209 | |
|
210 | 0 | if (ret == sizeof(buf)) { |
211 | | /* Success, stir in the entropy */ |
212 | 0 | sha256_process(hs, (void*)buf, sizeof(buf)); |
213 | 0 | return DROPBEAR_SUCCESS; |
214 | 0 | } |
215 | | |
216 | 0 | return DROPBEAR_FAILURE; |
217 | |
|
218 | 0 | } |
219 | | #endif /* HAVE_GETRANDOM */ |
220 | | |
221 | | /* Initialise the prng from /dev/urandom or prngd. This function can |
222 | | * be called multiple times */ |
223 | 0 | void seedrandom() { |
224 | 0 | hash_state hs; |
225 | |
|
226 | 0 | pid_t pid; |
227 | 0 | struct timeval tv; |
228 | 0 | clock_t clockval; |
229 | 0 | int urandom_seeded = 0; |
230 | |
|
231 | 0 | #if DROPBEAR_FUZZ |
232 | 0 | if (fuzz.fuzzing) { |
233 | 0 | return; |
234 | 0 | } |
235 | 0 | #endif |
236 | | |
237 | | /* hash in the new seed data */ |
238 | 0 | sha256_init(&hs); |
239 | | |
240 | | /* existing state */ |
241 | 0 | sha256_process(&hs, (void*)hashpool, sizeof(hashpool)); |
242 | |
|
243 | 0 | #ifdef HAVE_GETRANDOM |
244 | 0 | if (process_getrandom(&hs) == DROPBEAR_SUCCESS) { |
245 | 0 | urandom_seeded = 1; |
246 | 0 | } |
247 | 0 | #endif |
248 | |
|
249 | 0 | if (!urandom_seeded) { |
250 | | #if DROPBEAR_USE_PRNGD |
251 | | if (process_file(&hs, DROPBEAR_PRNGD_SOCKET, INIT_SEED_SIZE, 1) |
252 | | != DROPBEAR_SUCCESS) { |
253 | | dropbear_exit("Failure reading random device %s", |
254 | | DROPBEAR_PRNGD_SOCKET); |
255 | | urandom_seeded = 1; |
256 | | } |
257 | | #else |
258 | | /* non-blocking random source (probably /dev/urandom) */ |
259 | 0 | if (process_file(&hs, DROPBEAR_URANDOM_DEV, INIT_SEED_SIZE, 0) |
260 | 0 | != DROPBEAR_SUCCESS) { |
261 | 0 | dropbear_exit("Failure reading random device %s", |
262 | 0 | DROPBEAR_URANDOM_DEV); |
263 | 0 | urandom_seeded = 1; |
264 | 0 | } |
265 | 0 | #endif |
266 | 0 | } /* urandom_seeded */ |
267 | | |
268 | | /* A few other sources to fall back on. |
269 | | * Add more here for other platforms */ |
270 | 0 | #ifdef __linux__ |
271 | | /* Might help on systems with wireless */ |
272 | 0 | process_file(&hs, "/proc/interrupts", 0, 0); |
273 | |
|
274 | 0 | process_file(&hs, "/proc/loadavg", 0, 0); |
275 | 0 | process_file(&hs, "/proc/sys/kernel/random/entropy_avail", 0, 0); |
276 | | |
277 | | /* Mostly network visible but useful in some situations. |
278 | | * Limit size to avoid slowdowns on systems with lots of routes */ |
279 | 0 | process_file(&hs, "/proc/net/netstat", 4096, 0); |
280 | 0 | process_file(&hs, "/proc/net/dev", 4096, 0); |
281 | 0 | process_file(&hs, "/proc/net/tcp", 4096, 0); |
282 | | /* Also includes interface lo */ |
283 | 0 | process_file(&hs, "/proc/net/rt_cache", 4096, 0); |
284 | 0 | process_file(&hs, "/proc/vmstat", 0, 0); |
285 | 0 | #endif |
286 | |
|
287 | 0 | pid = getpid(); |
288 | 0 | sha256_process(&hs, (void*)&pid, sizeof(pid)); |
289 | | |
290 | | /* gettimeofday() doesn't completely fill out struct timeval on |
291 | | OS X (10.8.3), avoid valgrind warnings by clearing it first */ |
292 | 0 | memset(&tv, 0x0, sizeof(tv)); |
293 | 0 | gettimeofday(&tv, NULL); |
294 | 0 | sha256_process(&hs, (void*)&tv, sizeof(tv)); |
295 | |
|
296 | 0 | clockval = clock(); |
297 | 0 | sha256_process(&hs, (void*)&clockval, sizeof(clockval)); |
298 | | |
299 | | /* When a private key is read by the client or server it will |
300 | | * be added to the hashpool - see runopts.c */ |
301 | |
|
302 | 0 | sha256_done(&hs, hashpool); |
303 | |
|
304 | 0 | counter = 0; |
305 | 0 | donerandinit = 1; |
306 | | |
307 | | /* Feed it all back into /dev/urandom - this might help if Dropbear |
308 | | * is running from inetd and gets new state each time */ |
309 | 0 | write_urandom(); |
310 | 0 | } |
311 | | |
312 | | /* return len bytes of pseudo-random data */ |
313 | 61.5k | void genrandom(unsigned char* buf, unsigned int len) { |
314 | | |
315 | 61.5k | hash_state hs; |
316 | 61.5k | unsigned char hash[SHA256_HASH_SIZE]; |
317 | 61.5k | unsigned int copylen; |
318 | | |
319 | 61.5k | if (!donerandinit) { |
320 | 0 | dropbear_exit("seedrandom not done"); |
321 | 0 | } |
322 | | |
323 | 123k | while (len > 0) { |
324 | 61.5k | sha256_init(&hs); |
325 | 61.5k | sha256_process(&hs, (void*)hashpool, sizeof(hashpool)); |
326 | 61.5k | sha256_process(&hs, (void*)&counter, sizeof(counter)); |
327 | 61.5k | sha256_done(&hs, hash); |
328 | | |
329 | 61.5k | counter++; |
330 | 61.5k | if (counter > MAX_COUNTER) { |
331 | 0 | seedrandom(); |
332 | 0 | } |
333 | | |
334 | 61.5k | copylen = MIN(len, SHA256_HASH_SIZE); |
335 | 61.5k | memcpy(buf, hash, copylen); |
336 | 61.5k | len -= copylen; |
337 | 61.5k | buf += copylen; |
338 | 61.5k | } |
339 | 61.5k | m_burn(hash, sizeof(hash)); |
340 | 61.5k | } |
341 | | |
342 | | /* Generates a random mp_int. |
343 | | * max is a *mp_int specifying an upper bound. |
344 | | * rand must be an initialised *mp_int for the result. |
345 | | * the result rand satisfies: 0 < rand < max |
346 | | * */ |
347 | 0 | void gen_random_mpint(const mp_int *max, mp_int *rand) { |
348 | |
|
349 | 0 | unsigned char *randbuf = NULL; |
350 | 0 | unsigned int len = 0; |
351 | 0 | const unsigned char masks[] = {0xff, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f}; |
352 | |
|
353 | 0 | const int size_bits = mp_count_bits(max); |
354 | |
|
355 | 0 | len = size_bits / 8; |
356 | 0 | if ((size_bits % 8) != 0) { |
357 | 0 | len += 1; |
358 | 0 | } |
359 | |
|
360 | 0 | randbuf = (unsigned char*)m_malloc(len); |
361 | 0 | do { |
362 | 0 | genrandom(randbuf, len); |
363 | | /* Mask out the unrequired bits - mp_read_unsigned_bin expects |
364 | | * MSB first.*/ |
365 | 0 | randbuf[0] &= masks[size_bits % 8]; |
366 | |
|
367 | 0 | bytes_to_mp(rand, randbuf, len); |
368 | | |
369 | | /* keep regenerating until we get one satisfying |
370 | | * 0 < rand < max */ |
371 | 0 | } while (!(mp_cmp(rand, max) == MP_LT && mp_cmp_d(rand, 0) == MP_GT)); |
372 | 0 | m_burn(randbuf, len); |
373 | 0 | m_free(randbuf); |
374 | 0 | } |