Home | History | Annotate | Download | only in crypto
      1 /*
      2  * Random number generator
      3  * Copyright (c) 2010-2011, Jouni Malinen <j (at) w1.fi>
      4  *
      5  * This program is free software; you can redistribute it and/or modify
      6  * it under the terms of the GNU General Public License version 2 as
      7  * published by the Free Software Foundation.
      8  *
      9  * Alternatively, this software may be distributed under the terms of BSD
     10  * license.
     11  *
     12  * See README and COPYING for more details.
     13  *
     14  * This random number generator is used to provide additional entropy to the
     15  * one provided by the operating system (os_get_random()) for session key
     16  * generation. The os_get_random() output is expected to be secure and the
     17  * implementation here is expected to provide only limited protection against
     18  * cases where os_get_random() cannot provide strong randomness. This
     19  * implementation shall not be assumed to be secure as the sole source of
     20  * randomness. The random_get_bytes() function mixes in randomness from
     21  * os_get_random() and as such, calls to os_get_random() can be replaced with
     22  * calls to random_get_bytes() without reducing security.
     23  *
     24  * The design here follows partially the design used in the Linux
     25  * drivers/char/random.c, but the implementation here is simpler and not as
     26  * strong. This is a compromise to reduce duplicated CPU effort and to avoid
     27  * extra code/memory size. As pointed out above, os_get_random() needs to be
     28  * guaranteed to be secure for any of the security assumptions to hold.
     29  */
     30 
     31 #include "utils/includes.h"
     32 #ifdef __linux__
     33 #include <fcntl.h>
     34 #endif /* __linux__ */
     35 
     36 #include "utils/common.h"
     37 #include "utils/eloop.h"
     38 #include "sha1.h"
     39 #include "random.h"
     40 
     41 #define POOL_WORDS 32
     42 #define POOL_WORDS_MASK (POOL_WORDS - 1)
     43 #define POOL_TAP1 26
     44 #define POOL_TAP2 20
     45 #define POOL_TAP3 14
     46 #define POOL_TAP4 7
     47 #define POOL_TAP5 1
     48 #define EXTRACT_LEN 16
     49 #define MIN_READY_MARK 2
     50 
     51 static u32 pool[POOL_WORDS];
     52 static unsigned int input_rotate = 0;
     53 static unsigned int pool_pos = 0;
     54 static u8 dummy_key[20];
     55 #ifdef __linux__
     56 static size_t dummy_key_avail = 0;
     57 static int random_fd = -1;
     58 #endif /* __linux__ */
     59 static unsigned int own_pool_ready = 0;
     60 #define RANDOM_ENTROPY_SIZE 20
     61 static char *random_entropy_file = NULL;
     62 static int random_entropy_file_read = 0;
     63 
     64 #define MIN_COLLECT_ENTROPY 1000
     65 static unsigned int entropy = 0;
     66 static unsigned int total_collected = 0;
     67 
     68 
     69 static void random_write_entropy(void);
     70 
     71 
     72 static u32 __ROL32(u32 x, u32 y)
     73 {
     74 	return (x << (y & 31)) | (x >> (32 - (y & 31)));
     75 }
     76 
     77 
     78 static void random_mix_pool(const void *buf, size_t len)
     79 {
     80 	static const u32 twist[8] = {
     81 		0x00000000, 0x3b6e20c8, 0x76dc4190, 0x4db26158,
     82 		0xedb88320, 0xd6d6a3e8, 0x9b64c2b0, 0xa00ae278
     83 	};
     84 	const u8 *pos = buf;
     85 	u32 w;
     86 
     87 	wpa_hexdump_key(MSG_EXCESSIVE, "random_mix_pool", buf, len);
     88 
     89 	while (len--) {
     90 		w = __ROL32(*pos++, input_rotate & 31);
     91 		input_rotate += pool_pos ? 7 : 14;
     92 		pool_pos = (pool_pos - 1) & POOL_WORDS_MASK;
     93 		w ^= pool[pool_pos];
     94 		w ^= pool[(pool_pos + POOL_TAP1) & POOL_WORDS_MASK];
     95 		w ^= pool[(pool_pos + POOL_TAP2) & POOL_WORDS_MASK];
     96 		w ^= pool[(pool_pos + POOL_TAP3) & POOL_WORDS_MASK];
     97 		w ^= pool[(pool_pos + POOL_TAP4) & POOL_WORDS_MASK];
     98 		w ^= pool[(pool_pos + POOL_TAP5) & POOL_WORDS_MASK];
     99 		pool[pool_pos] = (w >> 3) ^ twist[w & 7];
    100 	}
    101 }
    102 
    103 
    104 static void random_extract(u8 *out)
    105 {
    106 	unsigned int i;
    107 	u8 hash[SHA1_MAC_LEN];
    108 	u32 *hash_ptr;
    109 	u32 buf[POOL_WORDS / 2];
    110 
    111 	/* First, add hash back to pool to make backtracking more difficult. */
    112 	hmac_sha1(dummy_key, sizeof(dummy_key), (const u8 *) pool,
    113 		  sizeof(pool), hash);
    114 	random_mix_pool(hash, sizeof(hash));
    115 	/* Hash half the pool to extra data */
    116 	for (i = 0; i < POOL_WORDS / 2; i++)
    117 		buf[i] = pool[(pool_pos - i) & POOL_WORDS_MASK];
    118 	hmac_sha1(dummy_key, sizeof(dummy_key), (const u8 *) buf,
    119 		  sizeof(buf), hash);
    120 	/*
    121 	 * Fold the hash to further reduce any potential output pattern.
    122 	 * Though, compromise this to reduce CPU use for the most common output
    123 	 * length (32) and return 16 bytes from instead of only half.
    124 	 */
    125 	hash_ptr = (u32 *) hash;
    126 	hash_ptr[0] ^= hash_ptr[4];
    127 	os_memcpy(out, hash, EXTRACT_LEN);
    128 }
    129 
    130 
    131 void random_add_randomness(const void *buf, size_t len)
    132 {
    133 	struct os_time t;
    134 	static unsigned int count = 0;
    135 
    136 	count++;
    137 	wpa_printf(MSG_MSGDUMP, "Add randomness: count=%u entropy=%u",
    138 		   count, entropy);
    139 	if (entropy > MIN_COLLECT_ENTROPY && (count & 0x3ff) != 0) {
    140 		/*
    141 		 * No need to add more entropy at this point, so save CPU and
    142 		 * skip the update.
    143 		 */
    144 		return;
    145 	}
    146 
    147 	os_get_time(&t);
    148 	wpa_hexdump_key(MSG_EXCESSIVE, "random pool",
    149 			(const u8 *) pool, sizeof(pool));
    150 	random_mix_pool(&t, sizeof(t));
    151 	random_mix_pool(buf, len);
    152 	wpa_hexdump_key(MSG_EXCESSIVE, "random pool",
    153 			(const u8 *) pool, sizeof(pool));
    154 	entropy++;
    155 	total_collected++;
    156 }
    157 
    158 
    159 int random_get_bytes(void *buf, size_t len)
    160 {
    161 	int ret;
    162 	u8 *bytes = buf;
    163 	size_t left;
    164 
    165 	wpa_printf(MSG_MSGDUMP, "Get randomness: len=%u entropy=%u",
    166 		   (unsigned int) len, entropy);
    167 
    168 	/* Start with assumed strong randomness from OS */
    169 	ret = os_get_random(buf, len);
    170 	wpa_hexdump_key(MSG_EXCESSIVE, "random from os_get_random",
    171 			buf, len);
    172 
    173 	/* Mix in additional entropy extracted from the internal pool */
    174 	left = len;
    175 	while (left) {
    176 		size_t siz, i;
    177 		u8 tmp[EXTRACT_LEN];
    178 		random_extract(tmp);
    179 		wpa_hexdump_key(MSG_EXCESSIVE, "random from internal pool",
    180 				tmp, sizeof(tmp));
    181 		siz = left > EXTRACT_LEN ? EXTRACT_LEN : left;
    182 		for (i = 0; i < siz; i++)
    183 			*bytes++ ^= tmp[i];
    184 		left -= siz;
    185 	}
    186 	wpa_hexdump_key(MSG_EXCESSIVE, "mixed random", buf, len);
    187 
    188 	if (entropy < len)
    189 		entropy = 0;
    190 	else
    191 		entropy -= len;
    192 
    193 	return ret;
    194 }
    195 
    196 
    197 int random_pool_ready(void)
    198 {
    199 #ifdef __linux__
    200 	int fd;
    201 	ssize_t res;
    202 
    203 	/*
    204 	 * Make sure that there is reasonable entropy available before allowing
    205 	 * some key derivation operations to proceed.
    206 	 */
    207 
    208 	if (dummy_key_avail == sizeof(dummy_key))
    209 		return 1; /* Already initialized - good to continue */
    210 
    211 	/*
    212 	 * Try to fetch some more data from the kernel high quality
    213 	 * /dev/random. There may not be enough data available at this point,
    214 	 * so use non-blocking read to avoid blocking the application
    215 	 * completely.
    216 	 */
    217 	fd = open("/dev/random", O_RDONLY | O_NONBLOCK);
    218 	if (fd < 0) {
    219 #ifndef CONFIG_NO_STDOUT_DEBUG
    220 		int error = errno;
    221 		perror("open(/dev/random)");
    222 		wpa_printf(MSG_ERROR, "random: Cannot open /dev/random: %s",
    223 			   strerror(error));
    224 #endif /* CONFIG_NO_STDOUT_DEBUG */
    225 		return -1;
    226 	}
    227 
    228 	res = read(fd, dummy_key + dummy_key_avail,
    229 		   sizeof(dummy_key) - dummy_key_avail);
    230 	if (res < 0) {
    231 		wpa_printf(MSG_ERROR, "random: Cannot read from /dev/random: "
    232 			   "%s", strerror(errno));
    233 		res = 0;
    234 	}
    235 	wpa_printf(MSG_DEBUG, "random: Got %u/%u bytes from "
    236 		   "/dev/random", (unsigned) res,
    237 		   (unsigned) (sizeof(dummy_key) - dummy_key_avail));
    238 	dummy_key_avail += res;
    239 	close(fd);
    240 
    241 	if (dummy_key_avail == sizeof(dummy_key)) {
    242 		if (own_pool_ready < MIN_READY_MARK)
    243 			own_pool_ready = MIN_READY_MARK;
    244 		random_write_entropy();
    245 		return 1;
    246 	}
    247 
    248 	wpa_printf(MSG_INFO, "random: Only %u/%u bytes of strong "
    249 		   "random data available from /dev/random",
    250 		   (unsigned) dummy_key_avail, (unsigned) sizeof(dummy_key));
    251 
    252 	if (own_pool_ready >= MIN_READY_MARK ||
    253 	    total_collected + 10 * own_pool_ready > MIN_COLLECT_ENTROPY) {
    254 		wpa_printf(MSG_INFO, "random: Allow operation to proceed "
    255 			   "based on internal entropy");
    256 		return 1;
    257 	}
    258 
    259 	wpa_printf(MSG_INFO, "random: Not enough entropy pool available for "
    260 		   "secure operations");
    261 	return 0;
    262 #else /* __linux__ */
    263 	/* TODO: could do similar checks on non-Linux platforms */
    264 	return 1;
    265 #endif /* __linux__ */
    266 }
    267 
    268 
    269 void random_mark_pool_ready(void)
    270 {
    271 	own_pool_ready++;
    272 	wpa_printf(MSG_DEBUG, "random: Mark internal entropy pool to be "
    273 		   "ready (count=%u/%u)", own_pool_ready, MIN_READY_MARK);
    274 	random_write_entropy();
    275 }
    276 
    277 
    278 #ifdef __linux__
    279 
    280 static void random_close_fd(void)
    281 {
    282 	if (random_fd >= 0) {
    283 		eloop_unregister_read_sock(random_fd);
    284 		close(random_fd);
    285 		random_fd = -1;
    286 	}
    287 }
    288 
    289 
    290 static void random_read_fd(int sock, void *eloop_ctx, void *sock_ctx)
    291 {
    292 	ssize_t res;
    293 
    294 	if (dummy_key_avail == sizeof(dummy_key)) {
    295 		random_close_fd();
    296 		return;
    297 	}
    298 
    299 	res = read(sock, dummy_key + dummy_key_avail,
    300 		   sizeof(dummy_key) - dummy_key_avail);
    301 	if (res < 0) {
    302 		wpa_printf(MSG_ERROR, "random: Cannot read from /dev/random: "
    303 			   "%s", strerror(errno));
    304 		return;
    305 	}
    306 
    307 	wpa_printf(MSG_DEBUG, "random: Got %u/%u bytes from /dev/random",
    308 		   (unsigned) res,
    309 		   (unsigned) (sizeof(dummy_key) - dummy_key_avail));
    310 	dummy_key_avail += res;
    311 
    312 	if (dummy_key_avail == sizeof(dummy_key)) {
    313 		random_close_fd();
    314 		if (own_pool_ready < MIN_READY_MARK)
    315 			own_pool_ready = MIN_READY_MARK;
    316 		random_write_entropy();
    317 	}
    318 }
    319 
    320 #endif /* __linux__ */
    321 
    322 
    323 static void random_read_entropy(void)
    324 {
    325 	char *buf;
    326 	size_t len;
    327 
    328 	if (!random_entropy_file)
    329 		return;
    330 
    331 	buf = os_readfile(random_entropy_file, &len);
    332 	if (buf == NULL)
    333 		return; /* entropy file not yet available */
    334 
    335 	if (len != 1 + RANDOM_ENTROPY_SIZE) {
    336 		wpa_printf(MSG_DEBUG, "random: Invalid entropy file %s",
    337 			   random_entropy_file);
    338 		os_free(buf);
    339 		return;
    340 	}
    341 
    342 	own_pool_ready = (u8) buf[0];
    343 	random_add_randomness(buf + 1, RANDOM_ENTROPY_SIZE);
    344 	random_entropy_file_read = 1;
    345 	os_free(buf);
    346 	wpa_printf(MSG_DEBUG, "random: Added entropy from %s "
    347 		   "(own_pool_ready=%u)",
    348 		   random_entropy_file, own_pool_ready);
    349 }
    350 
    351 
    352 static void random_write_entropy(void)
    353 {
    354 	char buf[RANDOM_ENTROPY_SIZE];
    355 	FILE *f;
    356 	u8 opr;
    357 
    358 	if (!random_entropy_file)
    359 		return;
    360 
    361 	random_get_bytes(buf, RANDOM_ENTROPY_SIZE);
    362 
    363 	f = fopen(random_entropy_file, "wb");
    364 	if (f == NULL) {
    365 		wpa_printf(MSG_ERROR, "random: Could not write %s",
    366 			   random_entropy_file);
    367 		return;
    368 	}
    369 
    370 	opr = own_pool_ready > 0xff ? 0xff : own_pool_ready;
    371 	fwrite(&opr, 1, 1, f);
    372 	fwrite(buf, RANDOM_ENTROPY_SIZE, 1, f);
    373 	fclose(f);
    374 
    375 	wpa_printf(MSG_DEBUG, "random: Updated entropy file %s "
    376 		   "(own_pool_ready=%u)",
    377 		   random_entropy_file, own_pool_ready);
    378 }
    379 
    380 
    381 void random_init(const char *entropy_file)
    382 {
    383 	os_free(random_entropy_file);
    384 	if (entropy_file)
    385 		random_entropy_file = os_strdup(entropy_file);
    386 	else
    387 		random_entropy_file = NULL;
    388 	random_read_entropy();
    389 
    390 #ifdef __linux__
    391 	if (random_fd >= 0)
    392 		return;
    393 
    394 	random_fd = open("/dev/random", O_RDONLY | O_NONBLOCK);
    395 	if (random_fd < 0) {
    396 #ifndef CONFIG_NO_STDOUT_DEBUG
    397 		int error = errno;
    398 		perror("open(/dev/random)");
    399 		wpa_printf(MSG_ERROR, "random: Cannot open /dev/random: %s",
    400 			   strerror(error));
    401 #endif /* CONFIG_NO_STDOUT_DEBUG */
    402 		return;
    403 	}
    404 	wpa_printf(MSG_DEBUG, "random: Trying to read entropy from "
    405 		   "/dev/random");
    406 
    407 	eloop_register_read_sock(random_fd, random_read_fd, NULL, NULL);
    408 #endif /* __linux__ */
    409 
    410 	random_write_entropy();
    411 }
    412 
    413 
    414 void random_deinit(void)
    415 {
    416 #ifdef __linux__
    417 	random_close_fd();
    418 #endif /* __linux__ */
    419 	random_write_entropy();
    420 	os_free(random_entropy_file);
    421 	random_entropy_file = NULL;
    422 }
    423