Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
branch: master
Fetching contributors…

Octocat-spinner-32-eaf2f5

Cannot retrieve contributors at this time

executable file 209 lines (144 sloc) 6.593 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
#!/usr/bin/env perl -lw

use strict;
use warnings;

# Configuration variables
my $Min_Length = 4;
my $Max_Length = 10;
my $Num_Words = 3;
my $Word_Separator = '.';
my $Words = "/usr/share/dict/words";

# Self test if --test is passed in
test() if @ARGV and $ARGV[0] eq '--test';

# Run the main program
print main();

# The main program
sub main {
    my @words;
    open my $fh, "<", $Words or die $!;
    my $count = 0;
    while (my $line = <$fh>) {
        chomp $line;

        # Ignore words that are too short or too long.
        next if length $line > $Max_Length or length $line < $Min_Length;

        # Guffa's algorithm for reading random lines out of a file without
        # storing them all in memory.
        $count++;
        if ( $count <= $Num_Words ) {
            $words[$count-1] = $line;
        } elsif ( rand($count) <= $Num_Words ) {
            $words[rand($Num_Words)] = $line;
        }
    }

    return join $Word_Separator, @words;
}

# Self test
sub test {
    require Test::More;
    Test::More->import;

    my $output = main();
    my @words = split m{\Q$Word_Separator\E}, $output;

    is( scalar @words, $Num_Words, "output $Num_Words words" );

    for my $word (@words) {
        like( $word, qr/^\w+$/, " '$word' is a word" );
        cmp_ok( length $word, '<=', $Max_Length, " it's not too long" );
        cmp_ok( length $word, '>=', $Min_Length, " and not too short" );
    }

    done_testing();
    exit;
}


__END__

=head1 NAME

dicepass - generate strong, pronouncable, rememberable passwords

=head1 SYNOPSIS

dicepass

=head1 DESCRIPTION

dicepass generates strong passwords you can remember by stringing
together dictionary words. It balances security with convenience to
ensure you, the user, will use good passwords.

Simply run the program from a command line and it will give you a
pretty good password. Don't like it? Run it again.

Some example passwords generated by dicepass:

laceless.ours.magneoptic
against.Cogswellia.mila
sobering.rukh.bromomania
gundy.consonance.tonetic

=head1 FAQ

=head2 Development

=head3 I want to help!

Great! We're on L<Github|https://github.com/schwern/dicepass>. Send
us an L<issue|https://github.com/schwern/dicepass/issues> and consider
L<making a contribution|https://help.github.com/articles/fork-a-repo>.

=head3 I have an idea to make dicepass even better!

Great! L<Tell us about it|https://github.com/schwern/dicepass/issues>!

=head3 I want to help, but I don't know how to use Git.

That's ok, you can L<edit this file in your browser|https://github.com/schwern/dicepass/edit/master/dicepass> and Github will take care of the rest.

=head3 I found a bug!

Great! Well... not great... but L<tell us about it|https://github.com/schwern/dicepass/issues>!

=head3 I'm not sure it's a bug...

L<Tell us about it|https://github.com/schwern/dicepass/issues> anyway!

Don't wait until you have a solution, don't worry if you don't have
all the information, tell us as soon as you have the problem. Any
problem, concern, improvement, feature, annoyance, typo... we want to
hear from you. Really! Nothing can be improved unless we know about
it.

=head2 Security

=head3 Aren't dictionary words insecure?

Yes, if your password is just a dictionary word. Even with easy to
guess replacements (the letter o to 0, for example) they can be easily
brute forced. The standard OS X dictionary of 200,000 words means only 200,000
passwords need to be checked, multipled by each common letter
replacement trick. Nothing for modern computers.

But with each word added, the problem becomes exponentially harder to
brute force. Picking three words at random from a 200,000 word
dictionary leads to 8,000,000,000,000,000 possibilities which is
pretty good.

See L<http://xkcd.com/936/>

=head3 Why use this instead of a random string?

A bunch of random characters is effectively impossible to crack, but
security is not just about numbers. The biggest security hole is
I<you> the human.

A good security system must be both secure I<and> convenient. If it
is not convenient, the users will simply circumvent it. If your
passwords are all impossible difficult to remember gobblty gook you
will use weaker passwords, or start reusing passwords.


=head3 Why use this instead of a browser based app?

Short version, you could be giving your password to an attacker.

Passwords are typically not directly stored, even encrypted. It is
"hashed", converted to a bunch of apparently random characters but in
such a way that only your password will produce that same bunch of
characters and the process cannot be reversed. This is also known as
a "checksum".

This is why a well run organization cannot tell you
what your password was if you forget it, they actually do not know.
When you log in, the password you enter is hashed and compared against
the hash in their database. If they match, they know you entered the
right password without knowing what your password is.

Attackers pass around huge lists of common passwords and common
variations. First, they will steal the hashed password file from an
organization. Then they will try everything on their list and see
what matches. If your password gets on that list, it doesn't matter
how clever or random it is, it doesn't matter if they don't know your
username, it is compromised.

A clever attacker would set up a web site to generate strong passwords
for users. Every password they hand out would go straight onto their
list, your password would be pre-compromised. Even if the site is
setup with the best of intentions, your connection to that site may be
insecure, or the site may have been quietly compromised, or your
browser could be compromised.

It's a simple risk to avoid.


=head3 The NSA has computers that can hack a million Gibsons!

Yes, with enough time and money you can brute force this system in a
not unreasonable amount of time. Quite honestly, you're not likely
worth the effort. If you I<are> worth the effort, you shouldn't be
picking your passwords using a program you downloaded off the
internet.

In addition, if a security organization wants to access your accounts,
having a stronger password is unlikely to save you. There are a
hundred faster and cheaper ways to crack your account than brute
forcing your password.


=head1 COPYRIGHT AND LICNESE

Copyright 2012 by Michael G Schwern E<lt>schwern@pobox.comE<gt>.

This program is free software; you can redistribute it and/or
modify it under the same terms as Perl itself.

See L<http://dev.perl.org/licenses/>

=cut

Something went wrong with that request. Please try again.