diff options
author | Damien Miller <djm@cvs.openbsd.org> | 2008-03-02 21:29:08 +0000 |
---|---|---|
committer | Damien Miller <djm@cvs.openbsd.org> | 2008-03-02 21:29:08 +0000 |
commit | df974a94d10240b2159aa0ae4842955efd9252c6 (patch) | |
tree | d0fef5d93ab5796bad8487ebb0dd043e731d25fa | |
parent | 54b368c4e9f5d7310129b5079d26619a2b48b14c (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/Makefile | 5 | ||||
-rw-r--r-- | share/man/man9/random.9 | 11 | ||||
-rw-r--r-- | sys/dev/rnd.c | 51 | ||||
-rw-r--r-- | sys/dev/rndvar.h | 3 | ||||
-rw-r--r-- | sys/netinet/ip_id.c | 4 |
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; |