Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
edwintorok authored and rwmjones committed Nov 14, 2013
1 parent f013b15 commit 6a10616
Show file tree
Hide file tree
Showing 4 changed files with 27 additions and 19 deletions.
11 changes: 1 addition & 10 deletions builder/builder.ml
Expand Up @@ -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 =
Expand Down
10 changes: 1 addition & 9 deletions mllib/password.ml
Expand Up @@ -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 =
Expand Down Expand Up @@ -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$"
Expand Down
21 changes: 21 additions & 0 deletions mllib/urandom.ml
Expand Up @@ -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
4 changes: 4 additions & 0 deletions mllib/urandom.mli
Expand Up @@ -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]. *)

0 comments on commit 6a10616

Please sign in to comment.