]>
git.ipfire.org Git - thirdparty/hostap.git/blob - src/crypto/random.c
2 * Random number generator
3 * Copyright (c) 2010-2011, Jouni Malinen <j@w1.fi>
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.
9 * Alternatively, this software may be distributed under the terms of BSD
12 * See README and COPYING for more details.
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.
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.
31 #include "utils/includes.h"
34 #endif /* __linux__ */
36 #include "utils/common.h"
37 #include "utils/eloop.h"
42 #define POOL_WORDS_MASK (POOL_WORDS - 1)
48 #define EXTRACT_LEN 16
49 #define MIN_READY_MARK 2
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];
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;
61 #define MIN_COLLECT_ENTROPY 1000
62 static unsigned int entropy
= 0;
63 static unsigned int total_collected
= 0;
66 static u32
__ROL32(u32 x
, u32 y
)
68 return (x
<< (y
& 31)) | (x
>> (32 - (y
& 31)));
72 static void random_mix_pool(const void *buf
, size_t len
)
74 static const u32 twist
[8] = {
75 0x00000000, 0x3b6e20c8, 0x76dc4190, 0x4db26158,
76 0xedb88320, 0xd6d6a3e8, 0x9b64c2b0, 0xa00ae278
81 wpa_hexdump_key(MSG_EXCESSIVE
, "random_mix_pool", buf
, len
);
84 w
= __ROL32(*pos
++, input_rotate
& 31);
85 input_rotate
+= pool_pos
? 7 : 14;
86 pool_pos
= (pool_pos
- 1) & POOL_WORDS_MASK
;
88 w
^= pool
[(pool_pos
+ POOL_TAP1
) & POOL_WORDS_MASK
];
89 w
^= pool
[(pool_pos
+ POOL_TAP2
) & POOL_WORDS_MASK
];
90 w
^= pool
[(pool_pos
+ POOL_TAP3
) & POOL_WORDS_MASK
];
91 w
^= pool
[(pool_pos
+ POOL_TAP4
) & POOL_WORDS_MASK
];
92 w
^= pool
[(pool_pos
+ POOL_TAP5
) & POOL_WORDS_MASK
];
93 pool
[pool_pos
] = (w
>> 3) ^ twist
[w
& 7];
98 static void random_extract(u8
*out
)
101 u8 hash
[SHA1_MAC_LEN
];
103 u32 buf
[POOL_WORDS
/ 2];
105 /* First, add hash back to pool to make backtracking more difficult. */
106 hmac_sha1(dummy_key
, sizeof(dummy_key
), (const u8
*) pool
,
108 random_mix_pool(hash
, sizeof(hash
));
109 /* Hash half the pool to extra data */
110 for (i
= 0; i
< POOL_WORDS
/ 2; i
++)
111 buf
[i
] = pool
[(pool_pos
- i
) & POOL_WORDS_MASK
];
112 hmac_sha1(dummy_key
, sizeof(dummy_key
), (const u8
*) buf
,
115 * Fold the hash to further reduce any potential output pattern.
116 * Though, compromise this to reduce CPU use for the most common output
117 * length (32) and return 16 bytes from instead of only half.
119 hash_ptr
= (u32
*) hash
;
120 hash_ptr
[0] ^= hash_ptr
[4];
121 os_memcpy(out
, hash
, EXTRACT_LEN
);
125 void random_add_randomness(const void *buf
, size_t len
)
128 static unsigned int count
= 0;
131 wpa_printf(MSG_MSGDUMP
, "Add randomness: count=%u entropy=%u",
133 if (entropy
> MIN_COLLECT_ENTROPY
&& (count
& 0x3ff) != 0) {
135 * No need to add more entropy at this point, so save CPU and
142 wpa_hexdump_key(MSG_EXCESSIVE
, "random pool",
143 (const u8
*) pool
, sizeof(pool
));
144 random_mix_pool(&t
, sizeof(t
));
145 random_mix_pool(buf
, len
);
146 wpa_hexdump_key(MSG_EXCESSIVE
, "random pool",
147 (const u8
*) pool
, sizeof(pool
));
153 int random_get_bytes(void *buf
, size_t len
)
159 wpa_printf(MSG_MSGDUMP
, "Get randomness: len=%u entropy=%u",
160 (unsigned int) len
, entropy
);
162 /* Start with assumed strong randomness from OS */
163 ret
= os_get_random(buf
, len
);
164 wpa_hexdump_key(MSG_EXCESSIVE
, "random from os_get_random",
167 /* Mix in additional entropy extracted from the internal pool */
173 wpa_hexdump_key(MSG_EXCESSIVE
, "random from internal pool",
175 siz
= left
> EXTRACT_LEN
? EXTRACT_LEN
: left
;
176 for (i
= 0; i
< siz
; i
++)
180 wpa_hexdump_key(MSG_EXCESSIVE
, "mixed random", buf
, len
);
191 int random_pool_ready(void)
198 * Make sure that there is reasonable entropy available before allowing
199 * some key derivation operations to proceed.
202 if (dummy_key_avail
== sizeof(dummy_key
))
203 return 1; /* Already initialized - good to continue */
206 * Try to fetch some more data from the kernel high quality
207 * /dev/random. There may not be enough data available at this point,
208 * so use non-blocking read to avoid blocking the application
211 fd
= open("/dev/random", O_RDONLY
| O_NONBLOCK
);
213 #ifndef CONFIG_NO_STDOUT_DEBUG
215 perror("open(/dev/random)");
216 wpa_printf(MSG_ERROR
, "random: Cannot open /dev/random: %s",
218 #endif /* CONFIG_NO_STDOUT_DEBUG */
222 res
= read(fd
, dummy_key
+ dummy_key_avail
,
223 sizeof(dummy_key
) - dummy_key_avail
);
225 wpa_printf(MSG_ERROR
, "random: Cannot read from /dev/random: "
226 "%s", strerror(errno
));
229 wpa_printf(MSG_DEBUG
, "random: Got %u/%u bytes from "
230 "/dev/random", (unsigned) res
,
231 (unsigned) (sizeof(dummy_key
) - dummy_key_avail
));
232 dummy_key_avail
+= res
;
235 if (dummy_key_avail
== sizeof(dummy_key
))
238 wpa_printf(MSG_INFO
, "random: Only %u/%u bytes of strong "
239 "random data available from /dev/random",
240 (unsigned) dummy_key_avail
, (unsigned) sizeof(dummy_key
));
242 if (own_pool_ready
>= MIN_READY_MARK
||
243 total_collected
+ 10 * own_pool_ready
> MIN_COLLECT_ENTROPY
) {
244 wpa_printf(MSG_INFO
, "random: Allow operation to proceed "
245 "based on internal entropy");
249 wpa_printf(MSG_INFO
, "random: Not enough entropy pool available for "
250 "secure operations");
252 #else /* __linux__ */
253 /* TODO: could do similar checks on non-Linux platforms */
255 #endif /* __linux__ */
259 void random_mark_pool_ready(void)
262 wpa_printf(MSG_DEBUG
, "random: Mark internal entropy pool to be "
263 "ready (count=%u/%u)", own_pool_ready
, MIN_READY_MARK
);
269 static void random_close_fd(void)
271 if (random_fd
>= 0) {
272 eloop_unregister_read_sock(random_fd
);
279 static void random_read_fd(int sock
, void *eloop_ctx
, void *sock_ctx
)
283 if (dummy_key_avail
== sizeof(dummy_key
)) {
288 res
= read(sock
, dummy_key
+ dummy_key_avail
,
289 sizeof(dummy_key
) - dummy_key_avail
);
291 wpa_printf(MSG_ERROR
, "random: Cannot read from /dev/random: "
292 "%s", strerror(errno
));
296 wpa_printf(MSG_DEBUG
, "random: Got %u/%u bytes from /dev/random",
298 (unsigned) (sizeof(dummy_key
) - dummy_key_avail
));
299 dummy_key_avail
+= res
;
301 if (dummy_key_avail
== sizeof(dummy_key
))
305 #endif /* __linux__ */
308 void random_init(void)
314 random_fd
= open("/dev/random", O_RDONLY
| O_NONBLOCK
);
316 #ifndef CONFIG_NO_STDOUT_DEBUG
318 perror("open(/dev/random)");
319 wpa_printf(MSG_ERROR
, "random: Cannot open /dev/random: %s",
321 #endif /* CONFIG_NO_STDOUT_DEBUG */
324 wpa_printf(MSG_DEBUG
, "random: Trying to read entropy from "
327 eloop_register_read_sock(random_fd
, random_read_fd
, NULL
, NULL
);
328 #endif /* __linux__ */
332 void random_deinit(void)
336 #endif /* __linux__ */