Commits

Toby Inkster committed f76447e

changes for 0.002 and 0.003

Comments (0)

Files changed (14)

examples/crypt-random.pl

+use feature 'say';
+use Crypt::Random;
+use Crypt::XkcdPassword;
+
+my $rng = sub { Crypt::Random::makerandom(Size => 12, Strength => 1) };
+
+say Crypt::XkcdPassword
+	-> new(rng => $rng)
+	-> make_password(4, qr{...});

examples/filter-profanity.pl

+use feature 'say';
+use Crypt::XkcdPassword;
+use Regexp::Common;
+
+say Crypt::XkcdPassword->make_password(4, sub {
+	not /$RE{profanity}/
+});

examples/filter-spelling.pl

+use feature 'say';
+use Crypt::XkcdPassword;
+use Text::Aspell;
+
+my $aspell = Text::Aspell->new;
+
+say Crypt::XkcdPassword->make_password(10, sub {
+	$aspell->check($_)
+});
+use feature 'say';
+use Crypt::XkcdPassword;
+use Regexp::Common;
+use Text::Aspell;
+
+my $aspell = Text::Aspell->new;
+
+say Crypt::XkcdPassword->make_password(4, sub {
+	/.{3}/
+	and $aspell->check($_)
+	and not /$RE{profanity}/
+});

lib/Crypt/XkcdPassword.pm

 package Crypt::XkcdPassword;
 
-use 5.010;
+use 5.010001;
 use Object::AUTHORITY;
 use utf8;
 
 BEGIN {
 	$Crypt::XkcdPassword::AUTHORITY = 'cpan:TOBYINK';
-	$Crypt::XkcdPassword::VERSION   = '0.001';
+	$Crypt::XkcdPassword::VERSION   = '0.003';
 }
 
 use Carp qw/carp croak/;
 use Class::Load qw/try_load_class/;
-
-use Mo qw/default/;
+use Moo;
 
 has rng => (
 	is      => 'rw',
-	isa     => 'CodeRef',
+	isa     => sub { ref $_[0] eq 'CODE' },
 	default => sub { sub { int(rand($_[0])) } },
 	);
 
 has words => (
 	is      => 'rw',
-	isa     => 'Str',
+	isa     => sub { !ref $_[0] },
 	default => sub { 'EN' },
 	);
 
 
 sub make_password
 {
-	my ($self, $length) = @_;
+	my ($self, $length, $filter) = @_;
 	
 	$self = $self->new unless ref $self;
 	
 	my $rng        = $self->rng;
 	my $words      = $self->_word_list;
 	my $word_count = @$words;
-	
-	return
-		join ' ',
-		map { $words->[$_] }
-		map { $rng->($word_count) }
-		1 .. $length;
+
+	my @password;
+	while (@password < $length)
+	{
+		local $_ = my $maybe = $words->[ $rng->($word_count) ];
+		push @password, $maybe
+			if (!defined $filter or $maybe ~~ $filter);
+	}
+
+	return join q{ }, @password;	
 }
 
 sub _word_list
 
 =over
 
-=item * C<< make_password($size) >>
+=item * C<< make_password($size, $filter) >>
 
 Returns the password as a string.
 
 English dictionary that provides over 47 bits of entropy; for the Italian
 dictionary (which has twice as many words), about 56 bits of entropy.
 
+$filter is a test against which each word is checked. It can be a sub
+returning true if the word is OK, or a regular expression matching OK
+words. Words which are not OK will be excluded from passwords. The default
+is to allow any words found in the provided dictionary.
+
 For reference, 47 bits of entropy is roughly equivalent to an eight digit
 random case-sensitive alphanumeric password (i.e. 62^8).
 
  say Crypt::XkcdPassword->make_password($size);
  say Crypt::XkcdPassword->new->make_password($size);
 
-Note that the passphrases returned may not be ASCII-safe, and may sometimes
-be inappropriate for uttering in polite company.
+Note that the passphrases returned may not be ASCII-safe, and may
+sometimes be inappropriate for uttering in polite company. See
+L<Crypt::XkcdPassword::Examples> for ways of using $filter to resolve
+this situation.
 
 =item * C<< chars >>
 
 
 =head1 SEE ALSO
 
+L<Crypt::XkcdPassword::Examples> - how to do stuff with this module.
+
 L<Data::SimplePassword> - I borrowed this module's interface, so it
 should mostly be possible to s/Data::SimplePassword/Crypt::XkcdPassword/.
 

lib/Crypt/XkcdPassword/Examples.pod

+=head1 NAME
+
+Crypt::XkcdPassword::Examples - practical uses of Crypt::XkcdPassword.
+
+=head1 FILTERING PROFANITY
+
+What is or is not offensive varies between contexts. L<Regexp::Common>
+includes a regular expression which matches many words commonly thought
+to be offensive, and may help you here.
+
+ use feature 'say';
+ use Crypt::XkcdPassword;
+ use Regexp::Common;
+ 
+ my $filter = sub { not /$RE{profanity}/ };
+ 
+ say Crypt::XkcdPassword->make_password(4, $filter);
+
+Bear in mind that even passphrases made up of individually inoffensive
+words could end up arranged in offensive ways. For example the five word
+pass phrase "i did you from behind" could be randomly generated (each of
+those five words are in the English dictionary).
+
+=head1 FILTERING NON-WORDS
+
+The defaut English dictionary supplied with Crypt::XkcdPassword is based
+on the most commonly used words in film scripts and television shows. It
+contains various colloquialisms, proper nouns and other terms that may be
+undesirable. 
+
+A solution is to filter words through a spell checker.
+
+ use feature 'say';
+ use Crypt::XkcdPassword;
+ use Text::Aspell;
+ 
+ my $aspell = Text::Aspell->new;
+ my $filter = sub { $aspell->check($_) };
+ 
+ say Crypt::XkcdPassword->make_password(4, $filter);
+
+=head1 COMBINING BOTH
+
+The following filter combines both of the above, and also filters out
+very short words:
+
+ use feature qw/say state/;
+ use Crypt::XkcdPassword;
+ use Regexp::Common;
+ use Text::Aspell;
+ 
+ my $filter = sub
+ {
+    state $aspell = Text::Aspell->new;
+    
+    /.{3}/
+    and $aspell->check($_)
+    and not /$RE{profanity}/
+ };
+ 
+ say Crypt::XkcdPassword->make_password(4, $filter);
+
+=head2 Why doesn't Crypt::XkcdPassword do this by default?
+
+It would add extra dependencies; not everybody would want it; but if you
+do want it, then it's not exactly a lot of work to do (the examples above
+show it's fairly simple).
+
+=head1 BETTER RANDOM NUMBERS
+
+The following uses Crypt::Random to generate better random numbers. (Note
+that it also includes a filter to ensure all words are at least three
+characters long.)
+
+ use feature 'say';
+ use Crypt::Random;
+ use Crypt::XkcdPassword;
+ 
+ my $rng = sub
+ {
+    Crypt::Random::makerandom(Size => 12, Strength => 1)
+ };
+ 
+ say Crypt::XkcdPassword
+    -> new(rng => $rng)
+    -> make_password(4, qr{...});
+
+=head1 BUGS
+
+Please report any bugs to
+L<http://rt.cpan.org/Dist/Display.html?Queue=Crypt-XkcdPassword>.
+
+=head1 SEE ALSO
+
+L<Crypt::XkcdPassword>.
+
+=head1 AUTHOR
+
+Toby Inkster E<lt>tobyink@cpan.orgE<gt>.
+
+=head1 COPYRIGHT AND LICENCE
+
+This software is copyright (c) 2012 by Toby Inkster.
+
+This is free software; you can redistribute it and/or modify it under
+the same terms as the Perl 5 programming language system itself.
+
+=head1 DISCLAIMER OF WARRANTIES
+
+THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+

lib/Crypt/XkcdPassword/Words/EN.pm

 package Crypt::XkcdPassword::Words::EN;
+BEGIN {
+	$Crypt::XkcdPassword::Words::EN::AUTHORITY = 'cpan:TOBYINK';
+	$Crypt::XkcdPassword::Words::EN::VERSION   = '0.003';
+}
 my @words;
 sub words
 {

lib/Crypt/XkcdPassword/Words/IT.pm

 package Crypt::XkcdPassword::Words::IT;
+BEGIN {
+	$Crypt::XkcdPassword::Words::IT::AUTHORITY = 'cpan:TOBYINK';
+	$Crypt::XkcdPassword::Words::IT::VERSION   = '0.003';
+}
 my @words;
 sub words
 {
 	:file-release   <http://backpan.cpan.org/authors/id/T/TO/TOBYINK/Crypt-XkcdPassword-0.001.tar.gz> ;
 	rdfs:label      "Initial release" .
 
+dist:project :release dist:v_0-002 .
+dist:v_0-002
+	a               :Version ;
+	dc:issued       "2012-02-03"^^xsd:date ;
+	:revision       "0.002"^^xsd:string ;
+	:file-release   <http://backpan.cpan.org/authors/id/T/TO/TOBYINK/Crypt-XkcdPassword-0.002.tar.gz> ;
+	dcs:changeset [
+		dcs:versus dist:v_0-001;
+		dcs:item   [rdfs:label "Forgot to run mo-inline in 0.001, creating an undeclared dependency on Mo. Have switched to Moo now anyway, and declared that as a dependency."@en;a dcs:Bugfix,dcs:Packaging];
+		].
+
+dist:project :release dist:v_0-003 .
+dist:v_0-003
+	a               :Version ;
+	dc:issued       "2012-03-06"^^xsd:date ;
+	:revision       "0.003"^^xsd:string ;
+	:file-release   <http://backpan.cpan.org/authors/id/T/TO/TOBYINK/Crypt-XkcdPassword-0.003.tar.gz> ;
+	dcs:changeset [
+		dcs:versus dist:v_0-002;
+		dcs:item   [rdfs:label "Provide a $filter parameter to make_password."@en; a dcs:Addition; dcs:fixes <http://purl.org/NET/cpan-uri/rt/ticket/74684>, <http://purl.org/NET/cpan-uri/rt/ticket/74685> ];
+		dcs:item   [rdfs:label "Crypt::XkcdPassword::Examples"@en; a dcs:Addition, dcs:Documentation ] ;
+		dcs:item   [rdfs:label "Require Perl 5.10.1 (Unicode in 5.10.0 is too broken)."@en; a dcs:Packaging ]
+		].
 	:version_from _:main;
 	:readme_from _:main;
 	:test_requires "Test::More 0.61";
-	:requires "Carp", "Class::Load", "utf8", "Object::AUTHORITY".
+	:requires "Carp", "Class::Load", "utf8", "Moo", "Object::AUTHORITY".
 
 _:main <http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#fileName> "lib/Crypt/XkcdPassword.pm".
 
+@prefix dbug: <http://ontologi.es/doap-bugs#> .
+@prefix dc: <http://purl.org/dc/terms/> .
+@prefix doap: <http://usefulinc.com/ns/doap#> .
+@prefix foaf: <http://xmlns.com/foaf/0.1/> .
+@prefix prio: <http://purl.org/NET/cpan-uri/rt/priority/> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+@prefix rt: <http://purl.org/NET/cpan-uri/rt/ticket/> .
+@prefix status: <http://purl.org/NET/cpan-uri/rt/status/> .
+@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
+
+_:r1330995075r0 a foaf:Agent ;
+	foaf:mbox <mailto:NEILB@cpan.org> .
+_:r1330995075r1 a foaf:Agent ;
+	foaf:nick "TOBYINK" .
+_:r1330995075r2 a foaf:Agent ;
+	foaf:nick "Nobody" .
+<http://purl.org/NET/cpan-uri/dist/Crypt-XkcdPassword/project> dbug:issue <http://purl.org/NET/cpan-uri/rt/ticket/74684>, <http://purl.org/NET/cpan-uri/rt/ticket/74685> .
+<http://purl.org/NET/cpan-uri/rt/ticket/74684> dbug:assignee _:r1330995075r1 ;
+	dbug:id "74684"^^xsd:string ;
+	dbug:page <https://rt.cpan.org/Ticket/Display.html?id=74684> ;
+	dbug:reporter _:r1330995075r0 ;
+	dbug:status status:open ;
+	dc:created "2012-02-04T23:45:32"^^xsd:dateTime ;
+	dc:modified "2012-02-27T23:39:42"^^xsd:dateTime ;
+	a dbug:Issue ;
+	rdfs:label "invalid words in the wordlist" .
+<http://purl.org/NET/cpan-uri/rt/ticket/74685> dbug:assignee _:r1330995075r2 ;
+	dbug:id "74685"^^xsd:string ;
+	dbug:page <https://rt.cpan.org/Ticket/Display.html?id=74685> ;
+	dbug:reporter _:r1330995075r0 ;
+	dbug:status status:rejected ;
+	dc:created "2012-02-04T23:47:34"^^xsd:dateTime ;
+	dc:modified "2012-02-06T22:53:11"^^xsd:dateTime ;
+	a dbug:Issue ;
+	rdfs:label "don't include swear words, or provide an option to decide" .
+use Test::More tests => 3;
+use Crypt::XkcdPassword;
+
+my $i;
+my $rng = sub { ++$i };
+my $gen = Crypt::XkcdPassword->new(rng => $rng);
+
+$i = 0;
+is
+	$gen->make_password(6),
+	'i to the a and that',
+	'no filter';
+
+$i = 0;
+is
+	$gen->make_password(6, sub { length $_ > 1 }),
+	'to the and that it of',
+	'filter sub';
+
+$i = 0;
+is
+	$gen->make_password(6, sub { !/e/ }),
+	'i to a and that it',
+	'filter regexp';
+

util/make_en_words.pl

-use 5.010;
+use 5.010001;
 use strict;
 use Web::Magic;
 
 print <<'HEADER';
 package Crypt::XkcdPassword::Words::EN;
+use 5.010001;
+BEGIN {
+	$Crypt::XkcdPassword::Words::EN::AUTHORITY = 'cpan:TOBYINK';
+	$Crypt::XkcdPassword::Words::EN::VERSION   = '0.003';
+}
 my @words;
 sub words
 {

util/make_it_words.pl

-use 5.010;
+use 5.010001;
 use strict;
 use utf8::all;
 use Web::Magic;
 
 print <<'HEADER';
 package Crypt::XkcdPassword::Words::IT;
+use 5.010001;
+BEGIN {
+	$Crypt::XkcdPassword::Words::IT::AUTHORITY = 'cpan:TOBYINK';
+	$Crypt::XkcdPassword::Words::IT::VERSION   = '0.003';
+}
 my @words;
 sub words
 {