patternMinor
Robust, know-entropy password generator
Viewed 0 times
knowrobustgeneratorpasswordentropy
Problem
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
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 = @{ $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
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 = @{ $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];
}
Solution
You don't need the bitmasks here, you can do with just basic arithmetic.
You're right that a naive modulo would generate biased letter combinations. Therefore the typical pattern is:
This pattern consumes less random numbers than yours and still generates a fair distribution.
Your code should not output a space at the end of the line.
To make your code testable, you should write a sub that generates a password from a given stream of bytes. Since this is the completely deterministic part, it's easy to test.
Make sure that the call to
You should use the same code pattern to generate the consonants and vowels, so that the code looks more symmetric. But that's a minor point.
You're right that a naive modulo would generate biased letter combinations. Therefore the typical pattern is:
$letters = "aeiouy";
$len = length($letters);
$limit = 255 / $len * $len; # this must be integer arithmetic
$limit = 255 - 255 % $len; # alternative spelling for the above line
while (($byte = next_byte()) >= $limit) {
next;
}
return $letters[$byte % $len];This pattern consumes less random numbers than yours and still generates a fair distribution.
Your code should not output a space at the end of the line.
To make your code testable, you should write a sub that generates a password from a given stream of bytes. Since this is the completely deterministic part, it's easy to test.
Make sure that the call to
read succeeds, since otherwise your randomness gets replaced with undefinedness.You should use the same code pattern to generate the consonants and vowels, so that the code looks more symmetric. But that's a minor point.
Code Snippets
$letters = "aeiouy";
$len = length($letters);
$limit = 255 / $len * $len; # this must be integer arithmetic
$limit = 255 - 255 % $len; # alternative spelling for the above line
while (($byte = next_byte()) >= $limit) {
next;
}
return $letters[$byte % $len];Context
StackExchange Code Review Q#153686, answer score: 5
Revisions (0)
No revisions yet.