From 31375d48cac5e8050957f1f18a9a4f5615d25711 Mon Sep 17 00:00:00 2001 From: Alejandro Colomar Date: Fri, 30 Dec 2022 19:46:09 +0100 Subject: [PATCH] Add csrand_uniform() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This API is similar to arc4random_uniform(3). However, for an input of 0, this function is equivalent to csrand(), while arc4random_uniform(0) returns 0. This function will be used to reimplement csrand_interval() as a wrapper around this one. The current implementation of csrand_interval() doesn't produce very good random numbers. It has a bias. And that comes from performing some unnecessary floating-point calculations that overcomplicate the problem. Looping until the random number hits within bounds is unbiased, and truncating unwanted bits makes the overhead of the loop very small. We could reduce loop overhead even more, by keeping unused bits of the random number, if the width of the mask is not greater than ULONG_WIDTH/2, however, that complicates the code considerably, and I prefer to be a bit slower but have simple code. BTW, Björn really deserves the copyright for csrand() (previously known as read_random_bytes()), since he rewrote it almost from scratch last year, and I kept most of its contents. Since he didn't put himself in the copyright back then, and BSD-3-Clause doesn't allow me to attribute derived works, I won't add his name, but if he asks, he should be put in the copyright too. Cc: "Jason A. Donenfeld" Cc: Cristian Rodríguez Cc: Adhemerval Zanella Cc: Björn Esser Cc: Yann Droneaud Cc: Joseph Myers Cc: Sam James Signed-off-by: Alejandro Colomar --- lib/prototypes.h | 1 + libmisc/csrand.c | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/lib/prototypes.h b/lib/prototypes.h index 3a04faa8..722d6d98 100644 --- a/lib/prototypes.h +++ b/lib/prototypes.h @@ -358,6 +358,7 @@ extern void pw_free (/*@out@*/ /*@only@*/struct passwd *pwent); /* csrand.c */ unsigned long csrand (void); +unsigned long csrand_uniform (unsigned long n); /* remove_tree.c */ extern int remove_tree (const char *root, bool remove_root); diff --git a/libmisc/csrand.c b/libmisc/csrand.c index a0237850..0cc99972 100644 --- a/libmisc/csrand.c +++ b/libmisc/csrand.c @@ -1,3 +1,9 @@ +/* + * SPDX-FileCopyrightText: Alejandro Colomar + * + * SPDX-License-Identifier: BSD-3-Clause + */ + #include #ident "$Id$" @@ -59,3 +65,23 @@ fail: fprintf(log_get_logfd(), _("Unable to obtain random bytes.\n")); exit(1); } + + +/* + * Return a uniformly-distributed CS random value in the interval [0, n-1]. + */ +unsigned long +csrand_uniform(unsigned long n) +{ + unsigned long r, max, mask; + + max = n - 1; + mask = bit_ceil_wrapul(n) - 1; + + do { + r = csrand(); + r &= mask; // optimization + } while (r > max); // p = ((mask + 1) % n) / (mask + 1); W.C.: p=0.5 + + return r; +}