My use case is I want a password generator that generates memorable and robust passwords for users, to give me a known minimum entropy.
For this - I'm approximating set of Consonant-Vowel-Consonant words, because then they are both memorable (ish) and have a known entropy level. The reason for this is because I'm not confident that when asked to pick a password, rules based 'systems' lead to at least some of the passwords being horribly weak.
So my core concern is whether the below has any security flaws - I believe that mapping /dev/urandom onto a flat character space should generally accomplish this, and the CVC groupings give me a known minimum entropy (44.9 bits in this case) over user-set passwords that I believe are often quite a lot worse across a whole set of user accounts.
cvc_gen.pl:
#!/usr/bin/env perl
use strict;
use warnings;
#uses /dev/urandom to fetch bytes.
#generates consonant-vowel-consonant groupings.
#each are 11.22 bits of entropy, meaning a 4-group is 45 bits.
#( 20 * 6 * 20 = 2400, which is 11.22 bits of entropy log2 2400
#log2(2400 ^ 4) = 44.91
#but because it's generated 'true random' it's a know entropy string.
my $num = 4;
my $format = "CVC";
my %letters = (
V => [qw ( a e i o u y )],
C => [ grep { not /[aeiouy]/ } "a" .. "z" ],
);
my %bitmask_for;
foreach my $type ( keys %letters ) {
#find the next power of 2 for the number of 'letters' in the set.
#So - for the '20' letter group, that's 31. (0x1F)
#And for the 6 letter group that's 7. (0x07)
$bitmask_for{$type} = ( 2 << log ( @{$letters{$type}} ) / log 2 ) - 1 ;
}
open( my $urandom, '<:raw', '/dev/urandom' ) or die $!;
for ( 1 .. $num ) {
for my $type ( split //, $format ) {
my $value;
while ( not defined $value or $value >= @{ $letters{$type} } ) {
my $byte;
read( $urandom, $byte, 1 );
#byte is 0-255. Our key space is 20 or 6.
#So rather than modulo, which would lead to an uneven distribution,
#we just bitmask at '1F' giving 0-31, and discard and 'too high'.
$value = (unpack "C", $byte ) & $bitmask_for{$type};
}
print $letters{$type}[$value];
}
print " ";
}
print "\n";
close($urandom);