NetBSD/sys/lib/libkern/arc4random.c

221 lines
5.6 KiB
C
Raw Normal View History

/* $NetBSD: arc4random.c,v 1.9 2002/10/06 13:42:36 dan Exp $ */
This commit includes two major changes: 1) Speed up arc4random(). We make arc4randbyte() inline, which makes this not much slower than, say, the other arc4 implementation in our kernel. We also replace four calls to arc4randbyte() with a loop, saving about 20% on some processors where the "unrolled" arc4randbyte() calls would needlessly stomp the cache. 2) Address various problems with the initialization/"stirring" code, primarily in the area of handling of the source data from the kernel entropy pool. We used to: a) Ask the entropy pool for 32 bytes b) If we got zero bytes, key with junk from the stack (ouch!) which has some nasty implications, to say the least. For example, we're most likely to get zero bytes at boot time, when the stack contents are even more predictable than usual. c) If we got less than 32 bytes but more than zero bytes, use however many bytes we got as the arc4 key, copying it repeatedly as per usual arc4 key setup. Because of the way NetBSD's entropy pool works, this was mostly harmless, because if you ask for RND_EXTRACT_ANY, you always get as many bytes as you ask for. However, this is probably a security hole in the original FreeBSD code, where AFAICT you might end up using an 8-bit arc4 key -- not good, much worse than using the output of the entropy pool hash function even when it thinks it only has 8 bits of entropy to give you. One thing this code could do on NetBSD that was not so good was to replace a key with a lot of entropy with one with less entropy. That's clearly counterproductive. The new code, instead: a) Asks for 32 good bytes. If it gets them, use them as the arc4 key in the usual way. b) Tracks how many entropy bytes the key it's replacing had. If the new entropy request got less bytes, leave the old key in place. Note that the first time through, the "old key" had zero bytes, so we'll always replace it. c) If we get less then 32 bytes but more than we had, request EXTRACT_ANY bytes from the entropy pool, padding the key out to 32 bytes which we then use as the arc4 key in the usual way. This is still really all rather backwards. Instead of this generator deciding to rekey itself using a basically arbitrary metric, it should register a callback so that the entropy pool code could rekey it when a lot of bits were available. Details at 11. Finally, rename the "stir" function (which did not stir) to "rekey", which is what it actually does.
2002-10-06 10:47:40 +04:00
/*-
* Copyright (c) 2002 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Thor Lancelot Simon.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the NetBSD
* Foundation, Inc. and its contributors.
* 4. Neither the name of The NetBSD Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
2002-05-28 14:09:24 +04:00
/*-
* THE BEER-WARE LICENSE
*
* <dan@FreeBSD.ORG> wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you
* think this stuff is worth it, you can buy me a beer in return.
*
* Dan Moschuk
*
* $FreeBSD: src/sys/libkern/arc4random.c,v 1.9 2001/08/30 12:30:58 bde Exp $
*/
#ifdef _KERNEL
#include "rnd.h"
#else
#define NRND 0
#endif
2002-05-28 14:09:24 +04:00
#include <sys/types.h>
#include <sys/time.h>
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
2002-05-29 10:27:15 +04:00
#include <lib/libkern/libkern.h>
2002-05-28 14:09:24 +04:00
#if NRND > 0
#include <sys/rnd.h>
2002-05-28 14:09:24 +04:00
#endif
#define ARC4_MAXRUNS 16384
#define ARC4_RESEED_SECONDS 300
#define ARC4_KEYBYTES 32 /* 256 bit key */
static u_int8_t arc4_i, arc4_j;
static int arc4_initialized = 0;
static int arc4_numruns = 0;
static u_int8_t arc4_sbox[256];
static struct timeval arc4_tv_nextreseed;
This commit includes two major changes: 1) Speed up arc4random(). We make arc4randbyte() inline, which makes this not much slower than, say, the other arc4 implementation in our kernel. We also replace four calls to arc4randbyte() with a loop, saving about 20% on some processors where the "unrolled" arc4randbyte() calls would needlessly stomp the cache. 2) Address various problems with the initialization/"stirring" code, primarily in the area of handling of the source data from the kernel entropy pool. We used to: a) Ask the entropy pool for 32 bytes b) If we got zero bytes, key with junk from the stack (ouch!) which has some nasty implications, to say the least. For example, we're most likely to get zero bytes at boot time, when the stack contents are even more predictable than usual. c) If we got less than 32 bytes but more than zero bytes, use however many bytes we got as the arc4 key, copying it repeatedly as per usual arc4 key setup. Because of the way NetBSD's entropy pool works, this was mostly harmless, because if you ask for RND_EXTRACT_ANY, you always get as many bytes as you ask for. However, this is probably a security hole in the original FreeBSD code, where AFAICT you might end up using an 8-bit arc4 key -- not good, much worse than using the output of the entropy pool hash function even when it thinks it only has 8 bits of entropy to give you. One thing this code could do on NetBSD that was not so good was to replace a key with a lot of entropy with one with less entropy. That's clearly counterproductive. The new code, instead: a) Asks for 32 good bytes. If it gets them, use them as the arc4 key in the usual way. b) Tracks how many entropy bytes the key it's replacing had. If the new entropy request got less bytes, leave the old key in place. Note that the first time through, the "old key" had zero bytes, so we'll always replace it. c) If we get less then 32 bytes but more than we had, request EXTRACT_ANY bytes from the entropy pool, padding the key out to 32 bytes which we then use as the arc4 key in the usual way. This is still really all rather backwards. Instead of this generator deciding to rekey itself using a basically arbitrary metric, it should register a callback so that the entropy pool code could rekey it when a lot of bits were available. Details at 11. Finally, rename the "stir" function (which did not stir) to "rekey", which is what it actually does.
2002-10-06 10:47:40 +04:00
static inline u_int8_t arc4_randbyte(void);
2002-05-28 14:09:24 +04:00
static __inline void
arc4_swap(u_int8_t *a, u_int8_t *b)
{
u_int8_t c;
c = *a;
*a = *b;
*b = c;
}
/*
* Stir our S-box.
*/
static void
This commit includes two major changes: 1) Speed up arc4random(). We make arc4randbyte() inline, which makes this not much slower than, say, the other arc4 implementation in our kernel. We also replace four calls to arc4randbyte() with a loop, saving about 20% on some processors where the "unrolled" arc4randbyte() calls would needlessly stomp the cache. 2) Address various problems with the initialization/"stirring" code, primarily in the area of handling of the source data from the kernel entropy pool. We used to: a) Ask the entropy pool for 32 bytes b) If we got zero bytes, key with junk from the stack (ouch!) which has some nasty implications, to say the least. For example, we're most likely to get zero bytes at boot time, when the stack contents are even more predictable than usual. c) If we got less than 32 bytes but more than zero bytes, use however many bytes we got as the arc4 key, copying it repeatedly as per usual arc4 key setup. Because of the way NetBSD's entropy pool works, this was mostly harmless, because if you ask for RND_EXTRACT_ANY, you always get as many bytes as you ask for. However, this is probably a security hole in the original FreeBSD code, where AFAICT you might end up using an 8-bit arc4 key -- not good, much worse than using the output of the entropy pool hash function even when it thinks it only has 8 bits of entropy to give you. One thing this code could do on NetBSD that was not so good was to replace a key with a lot of entropy with one with less entropy. That's clearly counterproductive. The new code, instead: a) Asks for 32 good bytes. If it gets them, use them as the arc4 key in the usual way. b) Tracks how many entropy bytes the key it's replacing had. If the new entropy request got less bytes, leave the old key in place. Note that the first time through, the "old key" had zero bytes, so we'll always replace it. c) If we get less then 32 bytes but more than we had, request EXTRACT_ANY bytes from the entropy pool, padding the key out to 32 bytes which we then use as the arc4 key in the usual way. This is still really all rather backwards. Instead of this generator deciding to rekey itself using a basically arbitrary metric, it should register a callback so that the entropy pool code could rekey it when a lot of bits were available. Details at 11. Finally, rename the "stir" function (which did not stir) to "rekey", which is what it actually does.
2002-10-06 10:47:40 +04:00
arc4_randrekey(void)
2002-05-28 14:09:24 +04:00
{
u_int8_t key[256];
This commit includes two major changes: 1) Speed up arc4random(). We make arc4randbyte() inline, which makes this not much slower than, say, the other arc4 implementation in our kernel. We also replace four calls to arc4randbyte() with a loop, saving about 20% on some processors where the "unrolled" arc4randbyte() calls would needlessly stomp the cache. 2) Address various problems with the initialization/"stirring" code, primarily in the area of handling of the source data from the kernel entropy pool. We used to: a) Ask the entropy pool for 32 bytes b) If we got zero bytes, key with junk from the stack (ouch!) which has some nasty implications, to say the least. For example, we're most likely to get zero bytes at boot time, when the stack contents are even more predictable than usual. c) If we got less than 32 bytes but more than zero bytes, use however many bytes we got as the arc4 key, copying it repeatedly as per usual arc4 key setup. Because of the way NetBSD's entropy pool works, this was mostly harmless, because if you ask for RND_EXTRACT_ANY, you always get as many bytes as you ask for. However, this is probably a security hole in the original FreeBSD code, where AFAICT you might end up using an 8-bit arc4 key -- not good, much worse than using the output of the entropy pool hash function even when it thinks it only has 8 bits of entropy to give you. One thing this code could do on NetBSD that was not so good was to replace a key with a lot of entropy with one with less entropy. That's clearly counterproductive. The new code, instead: a) Asks for 32 good bytes. If it gets them, use them as the arc4 key in the usual way. b) Tracks how many entropy bytes the key it's replacing had. If the new entropy request got less bytes, leave the old key in place. Note that the first time through, the "old key" had zero bytes, so we'll always replace it. c) If we get less then 32 bytes but more than we had, request EXTRACT_ANY bytes from the entropy pool, padding the key out to 32 bytes which we then use as the arc4 key in the usual way. This is still really all rather backwards. Instead of this generator deciding to rekey itself using a basically arbitrary metric, it should register a callback so that the entropy pool code could rekey it when a lot of bits were available. Details at 11. Finally, rename the "stir" function (which did not stir) to "rekey", which is what it actually does.
2002-10-06 10:47:40 +04:00
static int cur_keybytes;
int n, byteswanted;
#if NRND > 0
int r;
#endif
This commit includes two major changes: 1) Speed up arc4random(). We make arc4randbyte() inline, which makes this not much slower than, say, the other arc4 implementation in our kernel. We also replace four calls to arc4randbyte() with a loop, saving about 20% on some processors where the "unrolled" arc4randbyte() calls would needlessly stomp the cache. 2) Address various problems with the initialization/"stirring" code, primarily in the area of handling of the source data from the kernel entropy pool. We used to: a) Ask the entropy pool for 32 bytes b) If we got zero bytes, key with junk from the stack (ouch!) which has some nasty implications, to say the least. For example, we're most likely to get zero bytes at boot time, when the stack contents are even more predictable than usual. c) If we got less than 32 bytes but more than zero bytes, use however many bytes we got as the arc4 key, copying it repeatedly as per usual arc4 key setup. Because of the way NetBSD's entropy pool works, this was mostly harmless, because if you ask for RND_EXTRACT_ANY, you always get as many bytes as you ask for. However, this is probably a security hole in the original FreeBSD code, where AFAICT you might end up using an 8-bit arc4 key -- not good, much worse than using the output of the entropy pool hash function even when it thinks it only has 8 bits of entropy to give you. One thing this code could do on NetBSD that was not so good was to replace a key with a lot of entropy with one with less entropy. That's clearly counterproductive. The new code, instead: a) Asks for 32 good bytes. If it gets them, use them as the arc4 key in the usual way. b) Tracks how many entropy bytes the key it's replacing had. If the new entropy request got less bytes, leave the old key in place. Note that the first time through, the "old key" had zero bytes, so we'll always replace it. c) If we get less then 32 bytes but more than we had, request EXTRACT_ANY bytes from the entropy pool, padding the key out to 32 bytes which we then use as the arc4 key in the usual way. This is still really all rather backwards. Instead of this generator deciding to rekey itself using a basically arbitrary metric, it should register a callback so that the entropy pool code could rekey it when a lot of bits were available. Details at 11. Finally, rename the "stir" function (which did not stir) to "rekey", which is what it actually does.
2002-10-06 10:47:40 +04:00
if(!arc4_initialized)
/* The first time through, we must take what we can get */
byteswanted = 0;
else
/* Don't rekey with less entropy than we already have */
byteswanted = cur_keybytes;
#if NRND > 0 /* XXX without rnd, we will key from the stack, ouch! */
r = rnd_extract_data(key, ARC4_KEYBYTES, RND_EXTRACT_GOOD);
if (r < ARC4_KEYBYTES) {
if (r >= byteswanted) {
(void)rnd_extract_data(key + r,
ARC4_KEYBYTES - r,
RND_EXTRACT_ANY);
} else {
/* don't replace a good key with a bad one! */
arc4_tv_nextreseed = mono_time;
arc4_tv_nextreseed.tv_sec += ARC4_RESEED_SECONDS;
arc4_numruns = 0;
/* we should just ask rnd(4) to rekey us when
it can, but for now, we'll just try later. */
return;
}
2002-05-28 14:09:24 +04:00
}
This commit includes two major changes: 1) Speed up arc4random(). We make arc4randbyte() inline, which makes this not much slower than, say, the other arc4 implementation in our kernel. We also replace four calls to arc4randbyte() with a loop, saving about 20% on some processors where the "unrolled" arc4randbyte() calls would needlessly stomp the cache. 2) Address various problems with the initialization/"stirring" code, primarily in the area of handling of the source data from the kernel entropy pool. We used to: a) Ask the entropy pool for 32 bytes b) If we got zero bytes, key with junk from the stack (ouch!) which has some nasty implications, to say the least. For example, we're most likely to get zero bytes at boot time, when the stack contents are even more predictable than usual. c) If we got less than 32 bytes but more than zero bytes, use however many bytes we got as the arc4 key, copying it repeatedly as per usual arc4 key setup. Because of the way NetBSD's entropy pool works, this was mostly harmless, because if you ask for RND_EXTRACT_ANY, you always get as many bytes as you ask for. However, this is probably a security hole in the original FreeBSD code, where AFAICT you might end up using an 8-bit arc4 key -- not good, much worse than using the output of the entropy pool hash function even when it thinks it only has 8 bits of entropy to give you. One thing this code could do on NetBSD that was not so good was to replace a key with a lot of entropy with one with less entropy. That's clearly counterproductive. The new code, instead: a) Asks for 32 good bytes. If it gets them, use them as the arc4 key in the usual way. b) Tracks how many entropy bytes the key it's replacing had. If the new entropy request got less bytes, leave the old key in place. Note that the first time through, the "old key" had zero bytes, so we'll always replace it. c) If we get less then 32 bytes but more than we had, request EXTRACT_ANY bytes from the entropy pool, padding the key out to 32 bytes which we then use as the arc4 key in the usual way. This is still really all rather backwards. Instead of this generator deciding to rekey itself using a basically arbitrary metric, it should register a callback so that the entropy pool code could rekey it when a lot of bits were available. Details at 11. Finally, rename the "stir" function (which did not stir) to "rekey", which is what it actually does.
2002-10-06 10:47:40 +04:00
cur_keybytes = r;
for (n = ARC4_KEYBYTES; n < sizeof(key); n++)
key[n] = key[n % ARC4_KEYBYTES];
#endif
2002-05-28 14:09:24 +04:00
for (n = 0; n < 256; n++) {
arc4_j = (arc4_j + arc4_sbox[n] + key[n]) % 256;
arc4_swap(&arc4_sbox[n], &arc4_sbox[arc4_j]);
}
/* Reset for next reseed cycle. */
arc4_tv_nextreseed = mono_time;
arc4_tv_nextreseed.tv_sec += ARC4_RESEED_SECONDS;
arc4_numruns = 0;
/*
* Throw away the first N words of output, as suggested in the
* paper "Weaknesses in the Key Scheduling Algorithm of RC4"
* by Fluher, Mantin, and Shamir. (N = 256 in our case.)
*/
for (n = 0; n < 256 * 4; n++)
arc4_randbyte();
2002-05-28 14:09:24 +04:00
}
/*
* Initialize our S-box to its beginning defaults.
*/
static void
arc4_init(void)
{
int n;
arc4_i = arc4_j = 0;
for (n = 0; n < 256; n++)
arc4_sbox[n] = (u_int8_t) n;
This commit includes two major changes: 1) Speed up arc4random(). We make arc4randbyte() inline, which makes this not much slower than, say, the other arc4 implementation in our kernel. We also replace four calls to arc4randbyte() with a loop, saving about 20% on some processors where the "unrolled" arc4randbyte() calls would needlessly stomp the cache. 2) Address various problems with the initialization/"stirring" code, primarily in the area of handling of the source data from the kernel entropy pool. We used to: a) Ask the entropy pool for 32 bytes b) If we got zero bytes, key with junk from the stack (ouch!) which has some nasty implications, to say the least. For example, we're most likely to get zero bytes at boot time, when the stack contents are even more predictable than usual. c) If we got less than 32 bytes but more than zero bytes, use however many bytes we got as the arc4 key, copying it repeatedly as per usual arc4 key setup. Because of the way NetBSD's entropy pool works, this was mostly harmless, because if you ask for RND_EXTRACT_ANY, you always get as many bytes as you ask for. However, this is probably a security hole in the original FreeBSD code, where AFAICT you might end up using an 8-bit arc4 key -- not good, much worse than using the output of the entropy pool hash function even when it thinks it only has 8 bits of entropy to give you. One thing this code could do on NetBSD that was not so good was to replace a key with a lot of entropy with one with less entropy. That's clearly counterproductive. The new code, instead: a) Asks for 32 good bytes. If it gets them, use them as the arc4 key in the usual way. b) Tracks how many entropy bytes the key it's replacing had. If the new entropy request got less bytes, leave the old key in place. Note that the first time through, the "old key" had zero bytes, so we'll always replace it. c) If we get less then 32 bytes but more than we had, request EXTRACT_ANY bytes from the entropy pool, padding the key out to 32 bytes which we then use as the arc4 key in the usual way. This is still really all rather backwards. Instead of this generator deciding to rekey itself using a basically arbitrary metric, it should register a callback so that the entropy pool code could rekey it when a lot of bits were available. Details at 11. Finally, rename the "stir" function (which did not stir) to "rekey", which is what it actually does.
2002-10-06 10:47:40 +04:00
arc4_randrekey();
2002-05-28 14:09:24 +04:00
arc4_initialized = 1;
}
/*
* Generate a random byte.
*/
This commit includes two major changes: 1) Speed up arc4random(). We make arc4randbyte() inline, which makes this not much slower than, say, the other arc4 implementation in our kernel. We also replace four calls to arc4randbyte() with a loop, saving about 20% on some processors where the "unrolled" arc4randbyte() calls would needlessly stomp the cache. 2) Address various problems with the initialization/"stirring" code, primarily in the area of handling of the source data from the kernel entropy pool. We used to: a) Ask the entropy pool for 32 bytes b) If we got zero bytes, key with junk from the stack (ouch!) which has some nasty implications, to say the least. For example, we're most likely to get zero bytes at boot time, when the stack contents are even more predictable than usual. c) If we got less than 32 bytes but more than zero bytes, use however many bytes we got as the arc4 key, copying it repeatedly as per usual arc4 key setup. Because of the way NetBSD's entropy pool works, this was mostly harmless, because if you ask for RND_EXTRACT_ANY, you always get as many bytes as you ask for. However, this is probably a security hole in the original FreeBSD code, where AFAICT you might end up using an 8-bit arc4 key -- not good, much worse than using the output of the entropy pool hash function even when it thinks it only has 8 bits of entropy to give you. One thing this code could do on NetBSD that was not so good was to replace a key with a lot of entropy with one with less entropy. That's clearly counterproductive. The new code, instead: a) Asks for 32 good bytes. If it gets them, use them as the arc4 key in the usual way. b) Tracks how many entropy bytes the key it's replacing had. If the new entropy request got less bytes, leave the old key in place. Note that the first time through, the "old key" had zero bytes, so we'll always replace it. c) If we get less then 32 bytes but more than we had, request EXTRACT_ANY bytes from the entropy pool, padding the key out to 32 bytes which we then use as the arc4 key in the usual way. This is still really all rather backwards. Instead of this generator deciding to rekey itself using a basically arbitrary metric, it should register a callback so that the entropy pool code could rekey it when a lot of bits were available. Details at 11. Finally, rename the "stir" function (which did not stir) to "rekey", which is what it actually does.
2002-10-06 10:47:40 +04:00
static __inline u_int8_t
2002-05-28 14:09:24 +04:00
arc4_randbyte(void)
{
u_int8_t arc4_t;
arc4_i = (arc4_i + 1) % 256;
arc4_j = (arc4_j + arc4_sbox[arc4_i]) % 256;
arc4_swap(&arc4_sbox[arc4_i], &arc4_sbox[arc4_j]);
arc4_t = (arc4_sbox[arc4_i] + arc4_sbox[arc4_j]) % 256;
return arc4_sbox[arc4_t];
}
u_int32_t
arc4random(void)
{
u_int32_t ret;
This commit includes two major changes: 1) Speed up arc4random(). We make arc4randbyte() inline, which makes this not much slower than, say, the other arc4 implementation in our kernel. We also replace four calls to arc4randbyte() with a loop, saving about 20% on some processors where the "unrolled" arc4randbyte() calls would needlessly stomp the cache. 2) Address various problems with the initialization/"stirring" code, primarily in the area of handling of the source data from the kernel entropy pool. We used to: a) Ask the entropy pool for 32 bytes b) If we got zero bytes, key with junk from the stack (ouch!) which has some nasty implications, to say the least. For example, we're most likely to get zero bytes at boot time, when the stack contents are even more predictable than usual. c) If we got less than 32 bytes but more than zero bytes, use however many bytes we got as the arc4 key, copying it repeatedly as per usual arc4 key setup. Because of the way NetBSD's entropy pool works, this was mostly harmless, because if you ask for RND_EXTRACT_ANY, you always get as many bytes as you ask for. However, this is probably a security hole in the original FreeBSD code, where AFAICT you might end up using an 8-bit arc4 key -- not good, much worse than using the output of the entropy pool hash function even when it thinks it only has 8 bits of entropy to give you. One thing this code could do on NetBSD that was not so good was to replace a key with a lot of entropy with one with less entropy. That's clearly counterproductive. The new code, instead: a) Asks for 32 good bytes. If it gets them, use them as the arc4 key in the usual way. b) Tracks how many entropy bytes the key it's replacing had. If the new entropy request got less bytes, leave the old key in place. Note that the first time through, the "old key" had zero bytes, so we'll always replace it. c) If we get less then 32 bytes but more than we had, request EXTRACT_ANY bytes from the entropy pool, padding the key out to 32 bytes which we then use as the arc4 key in the usual way. This is still really all rather backwards. Instead of this generator deciding to rekey itself using a basically arbitrary metric, it should register a callback so that the entropy pool code could rekey it when a lot of bits were available. Details at 11. Finally, rename the "stir" function (which did not stir) to "rekey", which is what it actually does.
2002-10-06 10:47:40 +04:00
int i;
2002-05-28 14:09:24 +04:00
/* Initialize array if needed. */
if (!arc4_initialized)
arc4_init();
if ((++arc4_numruns > ARC4_MAXRUNS) ||
(mono_time.tv_sec > arc4_tv_nextreseed.tv_sec)) {
This commit includes two major changes: 1) Speed up arc4random(). We make arc4randbyte() inline, which makes this not much slower than, say, the other arc4 implementation in our kernel. We also replace four calls to arc4randbyte() with a loop, saving about 20% on some processors where the "unrolled" arc4randbyte() calls would needlessly stomp the cache. 2) Address various problems with the initialization/"stirring" code, primarily in the area of handling of the source data from the kernel entropy pool. We used to: a) Ask the entropy pool for 32 bytes b) If we got zero bytes, key with junk from the stack (ouch!) which has some nasty implications, to say the least. For example, we're most likely to get zero bytes at boot time, when the stack contents are even more predictable than usual. c) If we got less than 32 bytes but more than zero bytes, use however many bytes we got as the arc4 key, copying it repeatedly as per usual arc4 key setup. Because of the way NetBSD's entropy pool works, this was mostly harmless, because if you ask for RND_EXTRACT_ANY, you always get as many bytes as you ask for. However, this is probably a security hole in the original FreeBSD code, where AFAICT you might end up using an 8-bit arc4 key -- not good, much worse than using the output of the entropy pool hash function even when it thinks it only has 8 bits of entropy to give you. One thing this code could do on NetBSD that was not so good was to replace a key with a lot of entropy with one with less entropy. That's clearly counterproductive. The new code, instead: a) Asks for 32 good bytes. If it gets them, use them as the arc4 key in the usual way. b) Tracks how many entropy bytes the key it's replacing had. If the new entropy request got less bytes, leave the old key in place. Note that the first time through, the "old key" had zero bytes, so we'll always replace it. c) If we get less then 32 bytes but more than we had, request EXTRACT_ANY bytes from the entropy pool, padding the key out to 32 bytes which we then use as the arc4 key in the usual way. This is still really all rather backwards. Instead of this generator deciding to rekey itself using a basically arbitrary metric, it should register a callback so that the entropy pool code could rekey it when a lot of bits were available. Details at 11. Finally, rename the "stir" function (which did not stir) to "rekey", which is what it actually does.
2002-10-06 10:47:40 +04:00
arc4_randrekey();
2002-05-28 14:09:24 +04:00
}
This commit includes two major changes: 1) Speed up arc4random(). We make arc4randbyte() inline, which makes this not much slower than, say, the other arc4 implementation in our kernel. We also replace four calls to arc4randbyte() with a loop, saving about 20% on some processors where the "unrolled" arc4randbyte() calls would needlessly stomp the cache. 2) Address various problems with the initialization/"stirring" code, primarily in the area of handling of the source data from the kernel entropy pool. We used to: a) Ask the entropy pool for 32 bytes b) If we got zero bytes, key with junk from the stack (ouch!) which has some nasty implications, to say the least. For example, we're most likely to get zero bytes at boot time, when the stack contents are even more predictable than usual. c) If we got less than 32 bytes but more than zero bytes, use however many bytes we got as the arc4 key, copying it repeatedly as per usual arc4 key setup. Because of the way NetBSD's entropy pool works, this was mostly harmless, because if you ask for RND_EXTRACT_ANY, you always get as many bytes as you ask for. However, this is probably a security hole in the original FreeBSD code, where AFAICT you might end up using an 8-bit arc4 key -- not good, much worse than using the output of the entropy pool hash function even when it thinks it only has 8 bits of entropy to give you. One thing this code could do on NetBSD that was not so good was to replace a key with a lot of entropy with one with less entropy. That's clearly counterproductive. The new code, instead: a) Asks for 32 good bytes. If it gets them, use them as the arc4 key in the usual way. b) Tracks how many entropy bytes the key it's replacing had. If the new entropy request got less bytes, leave the old key in place. Note that the first time through, the "old key" had zero bytes, so we'll always replace it. c) If we get less then 32 bytes but more than we had, request EXTRACT_ANY bytes from the entropy pool, padding the key out to 32 bytes which we then use as the arc4 key in the usual way. This is still really all rather backwards. Instead of this generator deciding to rekey itself using a basically arbitrary metric, it should register a callback so that the entropy pool code could rekey it when a lot of bits were available. Details at 11. Finally, rename the "stir" function (which did not stir) to "rekey", which is what it actually does.
2002-10-06 10:47:40 +04:00
for(i = 0, ret = 0; i < 24; ret |= arc4_randbyte() << i, i += 8);
2002-05-28 14:09:24 +04:00
return ret;
}
void
arc4randbytes(void *p, size_t len)
{
u_int8_t *buf;
size_t i;
buf = (u_int8_t *)p;
for(i = 0; i < len; buf[i] = arc4_randbyte(), i++);
arc4_numruns += len / sizeof(u_int32_t);
if((arc4_numruns > ARC4_MAXRUNS) ||
(mono_time.tv_sec > arc4_tv_nextreseed.tv_sec)) {
arc4_randrekey();
}
}