I am generating passwords by selecting random words from a dictionary and counting entropy=words_in_passphrase*log(dictionary_size)/log(2)
. This algorithm assumes words will be selected from the dictionary with uniform distribution.
I think the design principle is sound but I'm not sure about the implementation. I'm reading from /dev/urandom
enough bytes to express an index into the @words
dictionary, and converting those bytes into an integer somewhat awkwardly.
The code appears to work correctly, but are there any caveats to Perl or /dev/urandom which will skew the results? Full source.
my $entropy_per_word = log (@words) / log (2);
my $pw = "";
my $pw_entropy = 0;
# maybe /dev/random ?
open my $random_source, '<', "/dev/urandom" or die "Could not open /dev/urandom";
binmode $random_source or die $!;
my $random_buffer = "";
my $random_bytes = ceil ($entropy_per_word / 8);
die "Tried to read no random bytes." if ($random_bytes < 1);
while ($pw_entropy < $entropy)
{
die "Could not read random bytes"
if ($random_bytes !=
read ($random_source, $random_buffer, $random_bytes));
my @bytes = unpack 'C ' x $random_bytes, $random_buffer;
my $n = 0;
foreach (@bytes)
{
$n *= 256;
$n += $_ * 1;
}
next if ($n >= @words); # don't use %, it will bias the randomness
$pw .= ' ' if ("" ne $pw);
foreach (split //, $words[$n])
{
# sprinkle some capitalization for good measure
$_ = uc if (rand > 0.8);
$pw .= $_;
}
$pw_entropy += $entropy_per_word;
}