Slight overhaul of arc4random() and friends.

One bug fixed:  Use getmicrouptime() to trigger reseeds so that we
cannot be tricked by a clock being stepped backwards.

Express parameters in natural units and with natural names.

Don't use struct timeval more than we need to.

Various stylistic and readability polishing.

Introduce arc4rand(void *ptr, u_int len, int reseed) function which
returns a stream of pseudo-random bytes, observing the automatic
reseed criteria as well as allowing forced reseeds.

Rewrite arc4random() in terms of arc4rand().

Sponsored by:   DARPA & NAI Labs.
This commit is contained in:
Poul-Henning Kamp 2002-10-11 13:13:08 +00:00
parent a93cc5a1e7
commit 2c38619b52
3 changed files with 53 additions and 29 deletions

View File

@ -41,6 +41,8 @@
.Fn srandom "u_long seed"
.Ft u_long
.Fn random "void"
.Ft void
.Fn arc4rand "void *ptr" "u_int length" "int reseed"
.Ft u_int32_t
.Fn arc4random "void"
.Pp
@ -68,12 +70,17 @@ function is entirely predictable, and is therefore not of use where
knowledge of the sequence of numbers may be of benefit to an attacker.
.Pp
The
.Fn arc4random
.Fn arc4rand
function will return very good quality random numbers, slightly better
suited for security-related purposes.
The random numbers from
.Fn arc4random
.Fn arc4rand
are seeded from the entropy device if it is available.
Automatic reseeds happen after a certain timeinterval and after a
certain number of bytes have been delivered.
A forced reseed can be forced by passing a non-zero value in the
.Ar reseed
argument.
.Pp
The
.Fn read_random
@ -90,16 +97,22 @@ is filled with no more than
bytes. It is advised that
.Fn read_random
is not used; instead use
.Fn arc4random .
.Fn arc4rand
.Pp
All the bits generated by
.Fn random ,
.Fn arc4random
.Fn arc4rand
and
.Fn read_random
are usable. For example,
.Sq Li random()&01
will produce a random binary value.
.Pp
The
.Fn arc4random
is a convenience function which calls
.Fn arc4rand
to return a 32 bit pseudo-random integer.
.Sh RETURN VALUES
The
.Fn random
@ -114,10 +127,15 @@ The period of this random number generator is very large, approximately
.if n 16*((2**31)\(mi1).
.Pp
The
.Fn arc4rand
function uses the RC4 algorithm to generate successive pseudo-random
bytes.
The
.Fn arc4random
function
uses the RC4 algorithm to generate successive pseudo-random
numbers in the range from 0 to
uses
.Fn arc4rand
to generate pseudo-random numbers in the range from 0 to
.if t 2\u\s732\s10\d\(mi1.
.if n (2**32)\(mi1.
.Pp

View File

@ -15,15 +15,15 @@
#include <sys/libkern.h>
#include <sys/time.h>
#define ARC4_MAXRUNS 16384
#define ARC4_RESEED_BYTES 65536
#define ARC4_RESEED_SECONDS 300
#define ARC4_KEYBYTES 32 /* 256 bit key */
#define ARC4_KEYBYTES (256 / 8)
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;
static time_t arc4_t_reseed;
static u_int8_t arc4_randbyte(void);
@ -45,6 +45,7 @@ arc4_randomstir (void)
{
u_int8_t key[256];
int r, n;
struct timeval tv_now;
/*
* XXX read_random() returns unsafe numbers if the entropy
@ -52,21 +53,19 @@ arc4_randomstir (void)
*/
r = read_random(key, ARC4_KEYBYTES);
/* If r == 0 || -1, just use what was on the stack. */
if (r > 0)
{
if (r > 0) {
for (n = r; n < sizeof(key); n++)
key[n] = key[n % r];
}
for (n = 0; n < 256; n++)
{
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. */
getmicrotime(&arc4_tv_nextreseed);
arc4_tv_nextreseed.tv_sec += ARC4_RESEED_SECONDS;
getmicrouptime(&tv_now);
arc4_t_reseed = tv_now.tv_sec + ARC4_RESEED_SECONDS;
arc4_numruns = 0;
}
@ -111,27 +110,33 @@ arc4_randbyte(void)
return arc4_sbox[arc4_t];
}
u_int32_t
arc4random(void)
void
arc4rand(void *ptr, u_int len, int reseed)
{
u_int32_t ret;
struct timeval tv_now;
u_char *p;
struct timeval tv;
/* Initialize array if needed. */
if (!arc4_initialized)
arc4_init();
getmicrotime(&tv_now);
if ((++arc4_numruns > ARC4_MAXRUNS) ||
(tv_now.tv_sec > arc4_tv_nextreseed.tv_sec))
{
getmicrouptime(&tv);
arc4_numruns += len;
if (reseed ||
(arc4_numruns > ARC4_RESEED_BYTES) ||
(tv.tv_sec > arc4_t_reseed))
arc4_randomstir();
}
ret = arc4_randbyte();
ret |= arc4_randbyte() << 8;
ret |= arc4_randbyte() << 16;
ret |= arc4_randbyte() << 24;
p = ptr;
while (len--)
*p++ = arc4_randbyte();
}
uint32_t
arc4random(void)
{
uint32_t ret;
arc4rand(&ret, sizeof ret, 0);
return ret;
}

View File

@ -64,7 +64,8 @@ static __inline u_long ulmax(u_long a, u_long b) { return (a > b ? a : b); }
static __inline u_long ulmin(u_long a, u_long b) { return (a < b ? a : b); }
/* Prototypes for non-quad routines. */
u_int32_t arc4random(void);
uint32_t arc4random(void);
void arc4rand(void *ptr, u_int len, int reseed);
int bcmp(const void *, const void *, size_t);
void *bsearch(const void *, const void *, size_t,
size_t, int (*)(const void *, const void *));