The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
Changes 05
MANIFEST 13
META.yml 35
Makefile.PL 58
README 22
lib/Crypt/PBKDF2/Hash/DigestHMAC.pm 22
lib/Crypt/PBKDF2/Hash/HMACSHA1.pm 22
lib/Crypt/PBKDF2/Hash/HMACSHA2.pm 22
lib/Crypt/PBKDF2/Hash/HMACSHA3.pm 096
lib/Crypt/PBKDF2/Hash.pm 22
lib/Crypt/PBKDF2.pm 231
t/06-length-limit.t 025
12 files changed (This is a version diff) 21183
@@ -1,5 +1,10 @@
 Changes for Crypt::PBKDF2
 
+Version 0.142390: 2014-08-27
+  * Add HMACSHA3 hasher.
+  * Add an option to limit password length to prevent algorithmic
+    complexity attacks. Thanks Gabor Szabo for reporting.
+
 Version 0.140890: 2014-03-30
   * Remove use of Method::Signatures::Simple. Devel::Declare was causing
     problems for some users, and it's really not needed for such a simple
@@ -1,4 +1,4 @@
-# This file was automatically generated by Dist::Zilla::Plugin::Manifest v5.014.
+# This file was automatically generated by Dist::Zilla::Plugin::Manifest v5.020.
 Changes
 LICENSE
 MANIFEST
@@ -12,8 +12,10 @@ lib/Crypt/PBKDF2/Hash.pm
 lib/Crypt/PBKDF2/Hash/DigestHMAC.pm
 lib/Crypt/PBKDF2/Hash/HMACSHA1.pm
 lib/Crypt/PBKDF2/Hash/HMACSHA2.pm
+lib/Crypt/PBKDF2/Hash/HMACSHA3.pm
 t/01-raeburn.t
 t/02-validate.t
 t/03-clone.t
 t/04-params.t
 t/05-crypt-ldap.t
+t/06-length-limit.t
@@ -4,14 +4,15 @@ author:
   - 'Andrew Rodland <arodland@cpan.org>'
 build_requires:
   Encode: '0'
+  Test::Fatal: '0'
   Test::More: '0'
   constant: '0'
   strict: '0'
   warnings: '0'
 configure_requires:
-  ExtUtils::MakeMaker: '6.30'
+  ExtUtils::MakeMaker: '0'
 dynamic_config: 0
-generated_by: 'Dist::Zilla version 5.014, CPAN::Meta::Converter version 2.140640'
+generated_by: 'Dist::Zilla version 5.020, CPAN::Meta::Converter version 2.140640'
 license: perl
 meta-spec:
   url: http://module-build.sourceforge.net/META-spec-v1.4.html
@@ -25,6 +26,7 @@ requires:
   Digest: '1.16'
   Digest::HMAC: '1.01'
   Digest::SHA: '0'
+  Digest::SHA3: '0.22'
   MIME::Base64: '0'
   Module::Runtime: '0'
   Moose: '1'
@@ -37,5 +39,5 @@ resources:
   homepage: http://metacpan.org/release/Crypt-PBKDF2
   license: http://dev.perl.org/licenses/
   repository: git://github.com/arodland/Crypt-PBKDF2.git
-version: '0.140890'
+version: '0.142390'
 x_authority: cpan:ARODLAND
@@ -1,20 +1,19 @@
 
-# This file was automatically generated by Dist::Zilla::Plugin::MakeMaker v5.014.
+# This file was automatically generated by Dist::Zilla::Plugin::MakeMaker v5.020.
 use strict;
 use warnings;
 
 
 
-use ExtUtils::MakeMaker 6.30;
+use ExtUtils::MakeMaker ;
 
 
 
 my %WriteMakefileArgs = (
   "ABSTRACT" => "The PBKDF2 password hash algorithm",
   "AUTHOR" => "Andrew Rodland <arodland\@cpan.org>",
-  "BUILD_REQUIRES" => {},
   "CONFIGURE_REQUIRES" => {
-    "ExtUtils::MakeMaker" => "6.30"
+    "ExtUtils::MakeMaker" => 0
   },
   "DISTNAME" => "Crypt-PBKDF2",
   "EXE_FILES" => [],
@@ -25,6 +24,7 @@ my %WriteMakefileArgs = (
     "Digest" => "1.16",
     "Digest::HMAC" => "1.01",
     "Digest::SHA" => 0,
+    "Digest::SHA3" => "0.22",
     "MIME::Base64" => 0,
     "Module::Runtime" => 0,
     "Moose" => 1,
@@ -35,12 +35,13 @@ my %WriteMakefileArgs = (
   },
   "TEST_REQUIRES" => {
     "Encode" => 0,
+    "Test::Fatal" => 0,
     "Test::More" => 0,
     "constant" => 0,
     "strict" => 0,
     "warnings" => 0
   },
-  "VERSION" => "0.140890",
+  "VERSION" => "0.142390",
   "test" => {
     "TESTS" => "t/*.t"
   }
@@ -52,12 +53,14 @@ my %FallbackPrereqs = (
   "Digest" => "1.16",
   "Digest::HMAC" => "1.01",
   "Digest::SHA" => 0,
+  "Digest::SHA3" => "0.22",
   "Encode" => 0,
   "MIME::Base64" => 0,
   "Module::Runtime" => 0,
   "Moose" => 1,
   "Moose::Role" => 0,
   "Moose::Util::TypeConstraints" => 0,
+  "Test::Fatal" => 0,
   "Test::More" => 0,
   "Try::Tiny" => "0.04",
   "constant" => 0,
@@ -1,7 +1,7 @@
 
 
 This archive contains the distribution Crypt-PBKDF2,
-version 0.140890:
+version 0.142390:
 
   The PBKDF2 password hash algorithm
 
@@ -11,5 +11,5 @@ This is free software; you can redistribute it and/or modify it under
 the same terms as the Perl 5 programming language system itself.
 
 
-This README file was generated by Dist::Zilla::Plugin::Readme v5.014.
+This README file was generated by Dist::Zilla::Plugin::Readme v5.020.
 
@@ -1,6 +1,6 @@
 package Crypt::PBKDF2::Hash::DigestHMAC;
 # ABSTRACT: Digest::HMAC hash support for Crypt::PBKDF2.
-our $VERSION = '0.140890'; # VERSION
+our $VERSION = '0.142390'; # VERSION
 our $AUTHORITY = 'cpan:ARODLAND'; # AUTHORITY
 use Moose 1;
 use namespace::autoclean;
@@ -80,7 +80,7 @@ Crypt::PBKDF2::Hash::DigestHMAC - Digest::HMAC hash support for Crypt::PBKDF2.
 
 =head1 VERSION
 
-version 0.140890
+version 0.142390
 
 =head1 DESCRIPTION
 
@@ -1,6 +1,6 @@
 package Crypt::PBKDF2::Hash::HMACSHA1;
 # ABSTRACT: HMAC-SHA1 support for Crypt::PBKDF2 using Digest::SHA
-our $VERSION = '0.140890'; # VERSION
+our $VERSION = '0.142390'; # VERSION
 our $AUTHORITY = 'cpan:ARODLAND'; # AUTHORITY
 use Moose;
 use namespace::autoclean;
@@ -41,7 +41,7 @@ Crypt::PBKDF2::Hash::HMACSHA1 - HMAC-SHA1 support for Crypt::PBKDF2 using Digest
 
 =head1 VERSION
 
-version 0.140890
+version 0.142390
 
 =head1 DESCRIPTION
 
@@ -1,6 +1,6 @@
 package Crypt::PBKDF2::Hash::HMACSHA2;
 # ABSTRACT: HMAC-SHA2 support for Crypt::PBKDF2 using Digest::SHA
-our $VERSION = '0.140890'; # VERSION
+our $VERSION = '0.142390'; # VERSION
 our $AUTHORITY = 'cpan:ARODLAND'; # AUTHORITY
 use Moose 1;
 use Moose::Util::TypeConstraints;
@@ -71,7 +71,7 @@ Crypt::PBKDF2::Hash::HMACSHA2 - HMAC-SHA2 support for Crypt::PBKDF2 using Digest
 
 =head1 VERSION
 
-version 0.140890
+version 0.142390
 
 =head1 DESCRIPTION
 
@@ -0,0 +1,96 @@
+package Crypt::PBKDF2::Hash::HMACSHA3;
+# ABSTRACT: HMAC-SHA3 support for Crypt::PBKDF2 using Digest::SHA
+our $VERSION = '0.142390'; # VERSION
+our $AUTHORITY = 'cpan:ARODLAND'; # AUTHORITY
+use Moose 1;
+use Moose::Util::TypeConstraints;
+use namespace::autoclean;
+use Digest::HMAC 1.01 ();
+use Digest::SHA3 0.22 ();
+
+with 'Crypt::PBKDF2::Hash';
+
+subtype 'SHASize' => (
+  as 'Int',
+  where { $_ == 224 or $_ == 256 or $_ == 384 or $_ == 512 },
+  message { "$_ is an invalid number of bits for SHA-3" }
+);
+
+has 'sha_size' => (
+  is => 'ro',
+  isa => 'SHASize',
+  default => 256,
+);
+
+has '_hasher' => (
+  is => 'ro',
+  lazy_build => 1,
+  init_arg => undef,
+);
+
+sub _build__hasher {
+  my $self = shift;
+  my $shasize = $self->sha_size;
+
+  return Digest::SHA3->can("sha3_$shasize");
+}
+
+sub hash_len {
+  my $self = shift;
+  return $self->sha_size() / 8;
+}
+
+sub generate {
+  my ($self, $data, $key) = @_;
+  return Digest::HMAC::hmac($data, $key, $self->_hasher);
+}
+
+sub to_algo_string {
+  my $self = shift;
+
+  return $self->sha_size;
+}
+
+sub from_algo_string {
+  my ($class, $str) = @_;
+
+  return $class->new( sha_size => $str );
+}
+
+__PACKAGE__->meta->make_immutable;
+1;
+
+__END__
+
+=pod
+
+=encoding UTF-8
+
+=head1 NAME
+
+Crypt::PBKDF2::Hash::HMACSHA3 - HMAC-SHA3 support for Crypt::PBKDF2 using Digest::SHA
+
+=head1 VERSION
+
+version 0.142390
+
+=head1 DESCRIPTION
+
+Uses L<Digest::HMAC> and L<Digest::SHA3> C<sha3_256>/C<sha3_384>/C<sha3_512>
+to provide the HMAC-ShA3 family of hashes for L<Crypt::PBKDF2>.
+
+This could be done with L<Crypt::PBKDF2::Hash::DigestHMAC> instead, but it
+seemed nice to have a uniform interface to HMACSHA*.
+
+=head1 AUTHOR
+
+Andrew Rodland <arodland@cpan.org>
+
+=head1 COPYRIGHT AND LICENSE
+
+This software is copyright (c) 2014 by Andrew Rodland.
+
+This is free software; you can redistribute it and/or modify it under
+the same terms as the Perl 5 programming language system itself.
+
+=cut
@@ -1,6 +1,6 @@
 package Crypt::PBKDF2::Hash;
 # ABSTRACT: Abstract role for PBKDF2 hashing algorithms.
-our $VERSION = '0.140890'; # VERSION
+our $VERSION = '0.142390'; # VERSION
 our $AUTHORITY = 'cpan:ARODLAND'; # AUTHORITY
 use Moose::Role;
 use namespace::autoclean;
@@ -27,7 +27,7 @@ Crypt::PBKDF2::Hash - Abstract role for PBKDF2 hashing algorithms.
 
 =head1 VERSION
 
-version 0.140890
+version 0.142390
 
 =head1 METHODS
 
@@ -1,6 +1,6 @@
 package Crypt::PBKDF2; 
 # ABSTRACT: The PBKDF2 password hashing algorithm.
-our $VERSION = '0.140890'; # VERSION
+our $VERSION = '0.142390'; # VERSION
 our $AUTHORITY = 'cpan:ARODLAND'; # AUTHORITY
 use Moose 1;
 use Moose::Util::TypeConstraints;
@@ -97,10 +97,21 @@ has encoding => (
 );
 
 
+has length_limit => (
+  is => 'ro',
+  isa => 'Int',
+  predicate => 'has_length_limit',
+);
+
+
 sub generate {
   my ($self, $password, $salt) = @_;
   $salt = $self->_random_salt unless defined $salt;
 
+  if ($self->has_length_limit and length($password) > $self->length_limit) {
+    croak "Password exceeds length limit";
+  }
+
   my $hash = $self->PBKDF2($salt, $password);
   return $self->encode_string($salt, $hash);
 }
@@ -108,6 +119,11 @@ sub generate {
 
 sub validate {
   my ($self, $hashed, $password) = @_;
+
+  if ($self->has_length_limit and length($password) > $self->length_limit) {
+    croak "Password exceeds length limit";
+  }
+
   my $info = $self->decode_string($hashed);
 
   my $hasher = try {
@@ -345,7 +361,7 @@ Crypt::PBKDF2 - The PBKDF2 password hashing algorithm.
 
 =head1 VERSION
 
-version 0.140890
+version 0.142390
 
 =head1 SYNOPSIS
 
@@ -439,6 +455,19 @@ Versions of this module up to 0.110461 generated the "crypt" format, so set
 that if you want it. Current versions of this module will read either format,
 but the "ldap" format is preferred.
 
+=head2 length_limit
+
+B<Type:> Integer
+
+The maximum password length to allow, for generate and verify functions.
+Allowing passwords of unlimited length can allow a denial-of-service attack
+in which an attacker asks the server to validate very large passwords.
+
+For compatibility this attribute is unset by default, but it is recommended
+to set it to a reasonably small value like 100 -- large enough that users
+aren't discouraged from having secure passwords, but small enough to limit
+the computation needed to validate any one password.
+
 =head1 METHODS
 
 =head2 generate ($password, [$salt])
@@ -0,0 +1,25 @@
+#!perl
+use strict;
+use warnings;
+
+use Test::More;
+use Test::Fatal;
+use Crypt::PBKDF2;
+
+my $pbkdf2 = Crypt::PBKDF2->new(
+  length_limit => 8
+);
+
+my $hash;
+
+is exception { $hash = $pbkdf2->generate("12345678") }, undef, "doesn't die on generate with valid length";
+is exception {
+  is $pbkdf2->validate($hash, "12345678"), 1, "hash validates"
+}, undef, "doesn't die on validate with valid length";
+
+like exception { $hash = $pbkdf2->generate("123456789") }, qr/length limit/, "dies on generate with too long pw";
+like exception {
+  is $pbkdf2->validate($hash, "123456789"), 1, "hash validates"
+}, qr/length limit/, "dies on validate with too long pw";
+
+done_testing;