summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDamien Miller <djm@cvs.openbsd.org>2008-03-02 21:29:08 +0000
committerDamien Miller <djm@cvs.openbsd.org>2008-03-02 21:29:08 +0000
commitdf974a94d10240b2159aa0ae4842955efd9252c6 (patch)
treed0fef5d93ab5796bad8487ebb0dd043e731d25fa
parent54b368c4e9f5d7310129b5079d26619a2b48b14c (diff)
Add a arc4random_uniform() that returns a uniformly distributed number
in the range 0 <= x < upper_bound Please use this new API instead of "arc4random() % upper_bound", as it avoids the "modulo bias" that favours small results when upper_bound is not a power of two. feedback deraadt@ mcbride@; ok deraadt@
-rw-r--r--share/man/man9/Makefile5
-rw-r--r--share/man/man9/random.911
-rw-r--r--sys/dev/rnd.c51
-rw-r--r--sys/dev/rndvar.h3
-rw-r--r--sys/netinet/ip_id.c4
5 files changed, 65 insertions, 9 deletions
diff --git a/share/man/man9/Makefile b/share/man/man9/Makefile
index 3a78a60932e..da1dfd6426c 100644
--- a/share/man/man9/Makefile
+++ b/share/man/man9/Makefile
@@ -1,4 +1,4 @@
-# $OpenBSD: Makefile,v 1.137 2007/11/28 23:24:07 art Exp $
+# $OpenBSD: Makefile,v 1.138 2008/03/02 21:29:06 djm Exp $
# $NetBSD: Makefile,v 1.4 1996/01/09 03:23:01 thorpej Exp $
# Makefile for section 9 (kernel function and variable) manual pages.
@@ -254,7 +254,8 @@ MLINKS+=random.9 add_true_randomness.9 \
random.9 add_audio_randomness.9 \
random.9 get_random_bytes.9 \
random.9 arc4random.9 \
- random.9 arc4random_bytes.9
+ random.9 arc4random_bytes.9 \
+ random.9 arc4random_uniform.9
MLINKS+=rasops.9 rasops_init.9 rasops.9 rasops_reconfig.9
MLINKS+=rssadapt.9 ieee80211_rssadapt_choose.9 \
rssadapt.9 ieee80211_rssadapt_input.9 \
diff --git a/share/man/man9/random.9 b/share/man/man9/random.9
index b17faf8d00e..ac4246dceba 100644
--- a/share/man/man9/random.9
+++ b/share/man/man9/random.9
@@ -1,4 +1,4 @@
-.\" $OpenBSD: random.9,v 1.22 2007/05/31 19:20:01 jmc Exp $
+.\" $OpenBSD: random.9,v 1.23 2008/03/02 21:29:07 djm Exp $
.\"
.\" Copyright (c) 1996,2000 Michael Shalayeff
.\" All rights reserved.
@@ -23,7 +23,7 @@
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\"
-.Dd $Mdocdate: May 31 2007 $
+.Dd $Mdocdate: March 2 2008 $
.Dt RND 9
.Os
.Sh NAME
@@ -51,6 +51,8 @@
.Fn arc4random "void"
.Ft void
.Fn arc4random_bytes "void *buf" "size_t nbytes"
+.Ft u_int32_t
+.Fn arc4random_uniform "u_int32_t upper_bound"
.Sh DESCRIPTION
The
.Fn add_mouse_randomness ,
@@ -87,6 +89,11 @@ and
will give random 32 bit numbers hashed with
the ARC4 algorithm, which appears to be faster and less abusive
to the entropy pool.
+.Pp
+.Fn arc4random_uniform
+will return a uniformly distributed random number less than
+.Fa upper_bound ,
+avoiding "modulo bias" when the upper bound is not a divisor of 2**32.
.Sh SEE ALSO
.Xr arc4random 3 ,
.Xr pchb 4 ,
diff --git a/sys/dev/rnd.c b/sys/dev/rnd.c
index 139a5a114f2..60df674f0aa 100644
--- a/sys/dev/rnd.c
+++ b/sys/dev/rnd.c
@@ -1,9 +1,10 @@
-/* $OpenBSD: rnd.c,v 1.86 2007/12/29 08:03:05 dlg Exp $ */
+/* $OpenBSD: rnd.c,v 1.87 2008/03/02 21:29:07 djm Exp $ */
/*
* rnd.c -- A strong random number generator
*
* Copyright (c) 1996, 1997, 2000-2002 Michael Shalayeff.
+ * Copyright (c) 2008 Damien Miller.
*
* Version 1.89, last modified 19-Sep-99
*
@@ -241,6 +242,7 @@
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/disk.h>
+#include <sys/limits.h>
#include <sys/ioctl.h>
#include <sys/malloc.h>
#include <sys/fcntl.h>
@@ -574,7 +576,7 @@ arc4random_bytes_large(void *buf, size_t n)
mtx_leave(&rndlock);
rc4_keysetup(&lctx, lbuf, sizeof(lbuf));
- rc4_skip(&lctx, 256 * 4);
+ rc4_skip(&lctx, 256 * 4);
rc4_getbytes(&lctx, (u_char*)buf, n);
bzero(lbuf, sizeof(lbuf));
bzero(&lctx, sizeof(lctx));
@@ -598,6 +600,51 @@ arc4random_bytes(void *buf, size_t n)
mtx_leave(&rndlock);
}
+/*
+ * Calculate a uniformly distributed random number less than upper_bound
+ * avoiding "modulo bias".
+ *
+ * Uniformity is achieved by generating new random numbers until the one
+ * returned is outside the range [0, 2**32 % upper_bound). This
+ * guarantees the selected random number will be inside
+ * [2**32 % upper_bound, 2**32) which maps back to [0, upper_bound)
+ * after reduction modulo upper_bound.
+ */
+u_int32_t
+arc4random_uniform(u_int32_t upper_bound)
+{
+ u_int32_t r, min;
+
+ if (upper_bound < 2)
+ return 0;
+
+#if (ULONG_MAX > 0xffffffffUL)
+ min = 0x100000000UL % upper_bound;
+#else
+ /* Calculate (2**32 % upper_bound) avoiding 64-bit math */
+ if (upper_bound > 0x80000000)
+ min = 1 + ~upper_bound; /* 2**32 - upper_bound */
+ else {
+ /* (2**32 - (x * 2)) % x == 2**32 % x when x <= 2**31 */
+ min = ((0xffffffff - (upper_bound << 2)) + 1) % upper_bound;
+ }
+#endif
+
+ /*
+ * This could theoretically loop forever but each retry has
+ * p > 0.5 (worst case, usually far better) of selecting a
+ * number inside the range we need, so it should rarely need
+ * to re-roll.
+ */
+ for (;;) {
+ r = arc4random();
+ if (r >= min)
+ break;
+ }
+
+ return r % upper_bound;
+}
+
void
randomattach(void)
{
diff --git a/sys/dev/rndvar.h b/sys/dev/rndvar.h
index 60305d48fa6..c6831502c0b 100644
--- a/sys/dev/rndvar.h
+++ b/sys/dev/rndvar.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: rndvar.h,v 1.19 2003/11/03 18:24:28 tedu Exp $ */
+/* $OpenBSD: rndvar.h,v 1.20 2008/03/02 21:29:07 djm Exp $ */
/*
* Copyright (c) 1996,2000 Michael Shalayeff.
@@ -87,6 +87,7 @@ void enqueue_randomness(int, int);
void get_random_bytes(void *, size_t);
void arc4random_bytes(void *, size_t);
u_int32_t arc4random(void);
+u_int32_t arc4random_uniform(u_int32_t);
#endif /* _KERNEL */
diff --git a/sys/netinet/ip_id.c b/sys/netinet/ip_id.c
index a8aef8c299b..b75d79940b7 100644
--- a/sys/netinet/ip_id.c
+++ b/sys/netinet/ip_id.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ip_id.c,v 1.16 2008/02/29 03:37:26 deraadt Exp $ */
+/* $OpenBSD: ip_id.c,v 1.17 2008/03/02 21:29:07 djm Exp $ */
/*
* Copyright (c) 2008 Theo de Raadt, Ryan McBride
@@ -59,7 +59,7 @@ ip_randomid(void)
ip_shuffle[i] = i;
for (i = sizeof(ip_shuffle)/sizeof(ip_shuffle[0]); --i; ) {
/* disregard the modulo bias because it is small */
- i2 = arc4random() % (i + 1);
+ i2 = arc4random_uniform(i + 1);
r = ip_shuffle[i];
ip_shuffle[i] = ip_shuffle[i2];
ip_shuffle[i2] = r;