On Tue, Dec 21, 2010 at 06:51:03PM +0100, Otto Moerbeek wrote:
> On Tue, Dec 21, 2010 at 05:59:33PM +0100, Kurt Knochner wrote:
> > OpenBSD uses arc4random() and arc4random_buf() all over the code to generate
> > random numbers. This code is defined in src/sys/dev/rnd.c.

> > [arc4stir] initializes the RC4 context with some random data, gathered by 
> > system
> > enthropy, that is mainly done by get_random_bytes().
> > 
> > ==> Bug #1
> > 
> > HOWEVER: Have a look at the buffer that's beeing used as a seed for the RC4
> > key setup. It's beeing filled with the random data, BUT at the beginning it
> > will be filled with just the value of nanotime().
> > 
> > >        nanotime((struct timespec *) buf);
> > >        len = sizeof(buf) - sizeof(struct timespec);
> > >        get_random_bytes(buf + sizeof (struct timespec), len);
> > >        len += sizeof(struct timespec);
> > 
> > 
> > So, there is a lot of effort in get_random_bytes() to get "real random" data
> > for the buffer and then the value of nanotime() is prepended to the buffer?
> > That does not look right. Please consider: this buffer will be used as key
> > for  rc4_keysetup() and thus it should contain unrelated and unpredictable
> > data.
> 
> I don't know the answer to this question, but my guess is that the
> buffer is filled by nanotime() to cover the case that
> get_random_bytes() does not have enough entropy available, so at least
> some non-constant data is used. 

Wouldn't it be better to XOR this into the data returned from
get_random_bytes(), though? That'd get you optimal entropy in either
case.

Sample diff (which also removes the len variable):

Index: rnd.c
===================================================================
RCS file: /usr/cvs/src/src/sys/dev/rnd.c,v
retrieving revision 1.104
diff -u -p -r1.104 rnd.c
--- rnd.c       21 Nov 2010 22:58:40 -0000      1.104
+++ rnd.c       21 Dec 2010 18:19:35 -0000
@@ -779,13 +779,14 @@ get_random_bytes(void *buf, size_t nbyte
 static void
 arc4_stir(void)
 {
-       u_int8_t buf[256];
-       int len;
+       u_int8_t         buf[256];
+       struct timespec  ts;
+       int              i;
 
-       nanotime((struct timespec *) buf);
-       len = sizeof(buf) - sizeof(struct timespec);
-       get_random_bytes(buf + sizeof (struct timespec), len);
-       len += sizeof(struct timespec);
+       get_random_bytes(buf, sizeof(buf));
+       nanotime(&ts);
+       for (i = 0; i < sizeof(ts); i++)
+               buf[i] ^= ((uint8_t *) &ts)[i];
 
        mtx_enter(&rndlock);
        if (rndstats.arc4_nstirs > 0)
@@ -793,7 +794,7 @@ arc4_stir(void)
 
        rc4_keysetup(&arc4random_state, buf, sizeof(buf));
        arc4random_count = 0;
-       rndstats.arc4_stirs += len;
+       rndstats.arc4_stirs += sizeof(buf);
        rndstats.arc4_nstirs++;
 
        /*

> > ==> Bug #2
> > 
> > The function rc4_crypt() get's called as soon as rndstats.arc4_nstirs > 0.
> > This will be the case whenever arc4_stir get's called the second time (by
> > the timer reset - see above).
> > 
> > >        if (rndstats.arc4_nstirs > 0)
> > >                rc4_crypt(&arc4random_state, buf, buf, sizeof(buf));
> > 
> > >        rc4_keysetup(&arc4random_state, buf, sizeof(buf));
> > >        arc4random_count = 0;
> > >        rndstats.arc4_stirs += len;
> > >        rndstats.arc4_nstirs++;
> > 
> > HOWEVER, right after the call of rc4_crypt(), we call rc4_keysetup() with
> > the same 'arc4random_state'. This makes the call to rc4_crypt() useless, as
> > the data structure will be overwritten again with the init data of the RC4
> > function.
> 
> rc4_crypt() changes both the state and the contents of buf, since buf is
> used both as source and destination. That buf is used by rc4_keysetup()
> to create a new state. So indeed the state is overwritten, but the
> contents of buf produced by rc4_crypt() is used to do that.

Yes, that does seem right.

By the way, the arc4random_count variable doesn't seem to be used; is
there a reason not to garbage-collect it?

                Joachim

Reply via email to