Avoid modulo bias in random password generation

Char.code (input_char chan) mod nr_chars has modulo bias because
the original interval is not a multiple of the destination interval,
i.e. 256 mod nr_chars != 0.

One way to fix this is to keep generating random numbers until they fall outside
the interval where modulo bias occurs, that is accept only c=[256 % nr_chars, 256).
That interval maps back to [0, nr_chars), and has a length of
(256 - 256 % nr_chars), which is a multiple of nr_chars.

RWMJ:
 - Modify the code so it goes into a utility library.
 - Use the same code across virt-builder and virt-sysprep.
This commit is contained in:
Török Edwin
2013-11-14 11:13:50 +02:00
committed by Richard W.M. Jones
parent f013b15fa1
commit 6a1061663f
4 changed files with 27 additions and 19 deletions

View File

@@ -454,16 +454,7 @@ let main () =
*)
let chars =
"ABCDEFGHIJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz0123456789" in
let nr_chars = String.length chars in
let chan = open_in "/dev/urandom" in
let buf = String.create 16 in
for i = 0 to 15 do
buf.[i] <- chars.[Char.code (input_char chan) mod nr_chars]
done;
close_in chan;
buf
Urandom.urandom_uniform 16 chars
in
let root_password =

View File

@@ -57,7 +57,6 @@ and read_password_from_file filename =
(* Permissible characters in a salt. *)
let chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789./"
let nr_chars = String.length chars
let rec set_linux_passwords ~prog ?password_crypto g root passwords =
let crypto =
@@ -95,14 +94,7 @@ let rec set_linux_passwords ~prog ?password_crypto g root passwords =
*)
and encrypt password crypto =
(* Get random characters from the set [A-Za-z0-9./] *)
let salt =
let chan = open_in "/dev/urandom" in
let buf = String.create 16 in
for i = 0 to 15 do
buf.[i] <- chars.[Char.code (input_char chan) mod nr_chars]
done;
close_in chan;
buf in
let salt = Urandom.urandom_uniform 16 chars in
let salt =
(match crypto with
| `MD5 -> "$1$"

View File

@@ -46,3 +46,24 @@ let urandom_bytes n =
done;
close fd;
ret
(* Return a random number uniformly distributed in [0, upper_bound)
* avoiding modulo bias.
*)
let rec uniform_random read upper_bound =
let c = read () in
if c >= 256 mod upper_bound then c mod upper_bound
else uniform_random read upper_bound
let urandom_uniform n chars =
assert (n > 0);
let nr_chars = String.length chars in
assert (nr_chars > 0);
let ret = String.make n ' ' in
let fd = open_urandom_fd () in
for i = 0 to n-1 do
ret.[i] <- chars.[uniform_random (read_byte fd) nr_chars]
done;
close fd;
ret

View File

@@ -20,3 +20,7 @@
val urandom_bytes : int -> string
(** Read N bytes from /dev/urandom and return it as a binary string. *)
val urandom_uniform : int -> string -> string
(** [urandom_uniform n chars] returns [n] bytes, uniformly
distributed from the sets of characters [chars]. *)