Add (optional) scrypt support
[plack-middleware-auth-complex.git] / lib / Plack / Middleware / Auth / Complex.pm
index 59d9bc3b8df46972f6fb2d59c5afc081e09fc880..d4b5e4b997d218c76cb584f514da5ee5e3ff733e 100644 (file)
@@ -58,6 +58,17 @@ sub new {
        my %self = $class->default_opts;
        %self = (%self, %$opts);
        $self{entropy_source} //= make_entropy_source;
        my %self = $class->default_opts;
        %self = (%self, %$opts);
        $self{entropy_source} //= make_entropy_source;
+       # If the user did not set [use_scrypt], we set it to 1 if scrypt
+       # is available and to 0 otherwise.
+       # If the user set [use_scrypt] to 1, we try to load scrypt and
+       # croak if we fail to do so.
+       unless (exists $self{use_scrypt}) {
+               my $success = eval 'use Authen::Passphrase::Scrypt';
+               $self{use_scrypt} = !!$success
+       }
+       if ($self{use_scrypt}) {
+               eval 'use Authen::Passphrase::Scrypt; 1' or croak "Failed to load Authen::Passphrase::Scrypt: $@\n";
+       }
        my $self = bless \%self, $class;
        $self
 }
        my $self = bless \%self, $class;
        $self
 }
@@ -94,18 +105,31 @@ sub check_passphrase {
        return $self->{cache}{$cachekey} if exists $self->{cache}{$cachekey}; # uncoverable branch true
        my $user = $self->get_user($username);
        return 0 unless $user;
        return $self->{cache}{$cachekey} if exists $self->{cache}{$cachekey}; # uncoverable branch true
        my $user = $self->get_user($username);
        return 0 unless $user;
-       my $ret = Authen::Passphrase->from_rfc2307($user->{passphrase})->match($passphrase);
+       my $ret;
+       if ($user->{passphrase} =~ /^{SCRYPT}/) {
+               croak "$username has a scrypt password but use_scrypt is false\n" unless $self->{use_scrypt};
+               $ret = Authen::Passphrase::Scrypt->from_rfc2307($user->{passphrase})
+       } else {
+               $ret = Authen::Passphrase->from_rfc2307($user->{passphrase});
+       }
+       $ret = $ret->match($passphrase);
        $self->{cache}{$cachekey} = $ret if $ret || $self->{cache_fail};
        $ret
 }
 
 sub hash_passphrase {
        my ($self, $passphrase) = @_;
        $self->{cache}{$cachekey} = $ret if $ret || $self->{cache_fail};
        $ret
 }
 
 sub hash_passphrase {
        my ($self, $passphrase) = @_;
-       Authen::Passphrase::BlowfishCrypt->new(
-               cost => 10,
-               passphrase => $passphrase,
-               salt_random => 1,
-       )->as_rfc2307
+       if ($self->{use_scrypt}) {
+               Authen::Passphrase::Scrypt->new({
+                       passphrase => $passphrase,
+               })->as_rfc2307
+       } else {
+               Authen::Passphrase::BlowfishCrypt->new(
+                       cost => 10,
+                       passphrase => $passphrase,
+                       salt_random => 1,
+               )->as_rfc2307
+       }
 }
 
 sub set_passphrase {
 }
 
 sub set_passphrase {
@@ -355,6 +379,22 @@ possible, or the default entropy source otherwise. A warning is
 printed if the default entropy source is used, to supress it set this
 argument to the default entropy source.
 
 printed if the default entropy source is used, to supress it set this
 argument to the default entropy source.
 
+=item use_scrypt
+
+Boolean determining whether to use the scrypt algorithm via the
+C<Authen::Passphrase::Scrypt> module.
+
+If true, the default implementation of C<hash_passphrase> uses scrypt
+and C<check_passphrase> accepts scrypt passphrases (in addition to
+passphrases supported by C<Authen::Passphrase>).
+
+If false, the default implementation of C<hash_passphrase> uses bcrypt
+and C<check_passphrase> only accepts passphrases supported by
+C<Authen::Passphrase>.
+
+The default value is true if C<Authen::Passphrase::Scrypt> is
+installed, false otherwise.
+
 =item post_connect_cb
 
 Callback (coderef) that is called just after connecting to the
 =item post_connect_cb
 
 Callback (coderef) that is called just after connecting to the
@@ -472,13 +512,20 @@ address).
 =item B<check_passphrase>(I<$username>, I<$passphrase>)
 
 Returns true if the given plaintext passphrase matches the one
 =item B<check_passphrase>(I<$username>, I<$passphrase>)
 
 Returns true if the given plaintext passphrase matches the one
-obtained from database. Default implementation uses L<Authen::Passphrase>.
+obtained from database. Default implementation uses
+L<Authen::Passphrase> (and L<Authen::Passphrase::Scrypt> if
+C<use_scrypt> is true).
 
 =item B<hash_passphrase>(I<$passphrase>)
 
 
 =item B<hash_passphrase>(I<$passphrase>)
 
-Returns a RFC2307-formatted hash of the passphrase. Default
-implementation uses L<Authen::Passphrase::BlowfishCrypt> with a cost
-of 10 and a random salt.
+Returns a RFC2307-formatted hash of the passphrase.
+
+If C<use_scrypt> is true, default implementation uses
+L<Authen::Passphrase::Scrypt> with default parameters.
+
+If C<use_scrypt> is false, default implementation uses
+L<Authen::Passphrase::BlowfishCrypt> with a cost of 10 and a random
+salt.
 
 =item B<set_passphrase>(I<$username>, I<$passphrase>)
 
 
 =item B<set_passphrase>(I<$username>, I<$passphrase>)
 
This page took 0.011663 seconds and 4 git commands to generate.