The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
use 5.008001;
use strict;
use warnings;

package Crypt::Diceware;
# ABSTRACT: Random passphrase generator loosely based on the Diceware algorithm
our $VERSION = '0.005'; # VERSION

use Class::Load qw/load_class/;
use Crypt::Rijndael;
use Crypt::URandom;
use Data::Entropy qw/with_entropy_source/;
use Data::Entropy::Algorithms qw/pick_r/;
use Data::Entropy::RawSource::CryptCounter;
use Data::Entropy::Source;

use Sub::Exporter -setup => {
    exports => [ words   => \'_build_words' ],
    groups  => { default => [qw/words/] },
};

my $ENTROPY = Data::Entropy::Source->new(
    Data::Entropy::RawSource::CryptCounter->new(
        Crypt::Rijndael->new( Crypt::URandom::urandom(32) )
    ),
    "getc"
);

sub _build_words {
    my ( $class, $name, $arg ) = @_;
    $arg ||= {};
    my $list;
    my $entropy = $arg->{entropy} || $ENTROPY;
    if ( exists $arg->{file} ) {
        my @list = do { local (@ARGV) = $arg->{file}; <> };
        chomp(@list);
        $list = \@list;
    }
    else {
        my $word_class = $arg->{wordlist} || 'Common';
        unless ( $word_class =~ /::/ ) {
            $word_class = "Crypt::Diceware::Wordlist::$word_class";
        }
        load_class($word_class);
        $list = do {
            no strict 'refs';
            \@{"${word_class}::Words"};
        };
    }
    return sub {
        my ($n) = @_;
        return unless $n && $n > 0;
        my @w = with_entropy_source(
            $entropy,
            sub {
                map { pick_r($list) } 1 .. int($n);
            }
        );
        return wantarray ? @w : join( ' ', @w );
    };
}

1;


# vim: ts=2 sts=2 sw=2 et:

__END__

=pod

=encoding UTF-8

=head1 NAME

Crypt::Diceware - Random passphrase generator loosely based on the Diceware algorithm

=head1 VERSION

version 0.005

=head1 SYNOPSIS

  use Crypt::Diceware;
  my @phrase = words(4); # qw/starker call recur outlaw/

  # with alternate word lists
  use Crypt::Diceware words => { wordlist => 'Original' };
  use Crypt::Diceware words => { wordlist => 'Beale' };

=head1 DESCRIPTION

This module generates a random passphrase of words based loosely on the
L<Diceware|http://world.std.com/~reinhold/diceware.html> algorithm by Arnold G.
Reinhold.

A Diceware passphrase consists of randomly selected words chosen from a list
of over seven thousand words.  A passphrase of four or five words is likely to
be stronger than typical human-generated passwords, which tend to be
too-short and over-sample common letters ("e") and numbers ("1").

Words are randomly selected using L<Data::Entropy> in AES counter mode,
seeded with L<Crypt::URandom>, which is reasonably cryptographically strong.

=head1 USAGE

By default, this module exports a single subroutine, C<words>, which uses the
L<Crypt::Diceware::Wordlist::Common> word list.

An alternate wordlist may be specified:

  use Crypt::Diceware words => { wordlist => 'Original' };

This loads the wordlist provided by
L<Crypt::Diceware::Wordlist::Original>. If the name of the wordlist
contains I<::> the name of the wordlist is not prefixed by
I<Crypt::Diceware::Wordlist>.

It is also possible to load a wordlist from a file via:

  use Crypt::Diceware words => { file => 'diceware-german.txt' };

The supplied file should contain one word per line.

You can also replace the entropy source with another L<Data::Entropy::Source>
object:

  use Crypt::Diceware words => { entropy => $entropy_source };

Exporting is done via L<Sub::Exporter> so any of its features may be used:

  use Crypt::Diceware words => { -as => 'passphrase' };
  my @phrase = passphrase(4);

=head2 words

  my @phrase = words(4);

Takes a positive numeric argument and returns a passphrase of that many
randomly-selected words. In a list context it will return a list of words, as above.
In a scalar context it will return a string with the words separated with a single space character:

  my $phrase = words(4);

Returns the empty list / string if the argument is missing or not a positive number.

=for Pod::Coverage method_names_here

=head1 SEE ALSO

Diceware and Crypt::Diceware related:

=over 4

=item *

L<Diceware|http://world.std.com/~reinhold/diceware.html>

=item *

L<Crypt::Diceware::Wordlist::Common>

=item *

L<Crypt::Diceware::Wordlist::Original>

=item *

L<Crypt::Diceware::Wordlist::Beale>

=back

Other CPAN passphrase generators:

=over 4

=item *

L<Crypt::PW44>

=item *

L<Crypt::XkcdPassword>

=item *

L<Review of CPAN password/phrase generators|http://neilb.org/reviews/passwords.html>

=back

About password strength in general:

=over 4

=item *

L<Password Strength (XKCD)|http://xkcd.com/936/>

=item *

L<Password Strength (Wikipedia)|http://en.wikipedia.org/wiki/Password_strength>

=back

=for :stopwords cpan testmatrix url annocpan anno bugtracker rt cpants kwalitee diff irc mailto metadata placeholders metacpan

=head1 SUPPORT

=head2 Bugs / Feature Requests

Please report any bugs or feature requests through the issue tracker
at L<https://github.com/dagolden/Crypt-Diceware/issues>.
You will be notified automatically of any progress on your issue.

=head2 Source Code

This is open source software.  The code repository is available for
public review and contribution under the terms of the license.

L<https://github.com/dagolden/Crypt-Diceware>

  git clone https://github.com/dagolden/Crypt-Diceware.git

=head1 AUTHOR

David Golden <dagolden@cpan.org>

=head1 CONTRIBUTORS

=over 4

=item *

Mario Domgoergen <mdom@taz.de>

=item *

Neil Bowers <neil@bowers.com>

=back

=head1 COPYRIGHT AND LICENSE

This software is Copyright (c) 2013 by David Golden.

This is free software, licensed under:

  The Apache License, Version 2.0, January 2004

=cut