From 48aaff06e641bde348b3d2cb959f2efab64008fa Mon Sep 17 00:00:00 2001 From: George Clark Date: Thu, 7 Dec 2017 19:01:00 -0500 Subject: [PATCH] Item14506: Implement Argon2i password hashing Fix an issue in the Password rest handlers -they were not passing through errors reported in the Password manager Add unit tests for argon2i, and update the test_htpasswd_auto test to bypass individual tests with missing dependencies, rather than skipping the whole test. --- .../Plugins/PasswordManagementPlugin/Core.pm | 23 ++- .../templates/passwordmessages.tmpl | 2 +- UnitTestContrib/test/unit/PasswordTests.pm | 133 ++++++++++++++---- core/data/System/ReleaseNotes02x02.txt | 22 ++- core/lib/Foswiki.spec | 51 +++++-- .../Configure/Checkers/Htpasswd/Encoding.pm | 7 + core/lib/Foswiki/Users/HtPasswdUser.pm | 63 ++++++++- 7 files changed, 248 insertions(+), 53 deletions(-) diff --git a/PasswordManagementPlugin/lib/Foswiki/Plugins/PasswordManagementPlugin/Core.pm b/PasswordManagementPlugin/lib/Foswiki/Plugins/PasswordManagementPlugin/Core.pm index d457b6b87a..9c536c047a 100644 --- a/PasswordManagementPlugin/lib/Foswiki/Plugins/PasswordManagementPlugin/Core.pm +++ b/PasswordManagementPlugin/lib/Foswiki/Plugins/PasswordManagementPlugin/Core.pm @@ -487,11 +487,13 @@ sub _RESTchangePassword { } unless ( $users->checkPassword( $login, $oldpassword ) ) { + my $error = $users->passwordError($login) || ''; throw Foswiki::OopsException( 'password', - web => $webName, - topic => $topic, - def => 'wrong_password' + web => $webName, + topic => $topic, + def => 'wrong_password', + params => [$error], ); } } @@ -515,9 +517,15 @@ sub _RESTchangePassword { } catch Error::Simple with { my $error = shift; - $result = $error->{-text}; + Foswiki::Func::writeWarning( "Error in setPassword: ", + ( split /\n/, $error->{-text} )[0] ); + $result = "Internal error"; }; + unless ($ok) { + $result ||= $users->passwordError($user) || ''; + } + if ( !$ok ) { throw Foswiki::OopsException( 'password', @@ -646,9 +654,10 @@ sub _RESTchangeEmail { unless ( $users->checkPassword( $login, $password ) ) { throw Foswiki::OopsException( 'password', - web => $webName, - topic => $topic, - def => 'wrong_password' + web => $webName, + topic => $topic, + def => 'wrong_password', + params => [ $users->passwordError($login) || '' ], ); } } diff --git a/PasswordManagementPlugin/templates/passwordmessages.tmpl b/PasswordManagementPlugin/templates/passwordmessages.tmpl index f59d228b6e..624b3c25b2 100644 --- a/PasswordManagementPlugin/templates/passwordmessages.tmpl +++ b/PasswordManagementPlugin/templates/passwordmessages.tmpl @@ -132,7 +132,7 @@ %{==============================================================================}% %TMPL:DEF{"wrong_password"}% ---+++ %MAKETEXT{"Incorrect Password"}% -%MAKETEXT{"The password you entered in the *old password* field is incorrect."}% +%MAKETEXT{"The password you entered in the *old password* field is incorrect."}% %PARAM1% %MAKETEXT{"Please go back in your browser and try again."}% %TMPL:END% diff --git a/UnitTestContrib/test/unit/PasswordTests.pm b/UnitTestContrib/test/unit/PasswordTests.pm index b0dd54cee9..84c7e1b153 100644 --- a/UnitTestContrib/test/unit/PasswordTests.pm +++ b/UnitTestContrib/test/unit/PasswordTests.pm @@ -91,11 +91,19 @@ sub doTests { $encrapted{$user} = $impl->fetchPass($user); $this->assert_null( $impl->error() ); $this->assert( $encrapted{$user} ); - $this->assert_str_equals( - $encrapted{$user}, - $impl->encrypt( $user, $this->{users1}->{$user}->{pass} ), - "fails for $user" - ); + + $this->assert( + $impl->checkPassword( $user, $this->{users1}->{$user}->{pass} ), + "checkPass failed" ); + +# argon2i generates a different key for each execution, cannot test by comparing hashes. + unless ( $encrapted{$user} =~ m/^\$argon2i\$/ ) { + $this->assert_str_equals( + $encrapted{$user}, + $impl->encrypt( $user, $this->{users1}->{$user}->{pass} ), + "fails for $user" + ); + } $this->assert_str_equals( $this->{users1}->{$user}->{emails}, join( ";", $impl->getEmails($user) ) @@ -107,7 +115,8 @@ sub doTests { $this->assert( $impl->checkPassword( $user, $this->{users1}->{$user}->{pass} ) ); $this->assert_str_equals( $encrapted{$user}, - $impl->encrypt( $user, $this->{users1}->{$user}->{pass} ) ); + $impl->encrypt( $user, $this->{users1}->{$user}->{pass} ) ) + unless ( $encrapted{$user} =~ m/^\$argon2i\$/ ); } # try changing with wrong pass @@ -252,9 +261,14 @@ sub skip { condition => { without_dep => 'Crypt::Eksblowfish::Bcrypt' }, tests => { 'PasswordTests::test_htpasswd_bcrypt' => - 'Missing Crypt::Eksblowfish::Bcrypt', - 'PasswordTests::test_htpasswd_auto' => - 'Missing Crypt::Eksblowfish::Bcrypt', + 'Missing Crypt::Argon2', + } + }, + { + condition => { without_dep => 'Crypt::Argon2' }, + tests => { + 'PasswordTests::test_htpasswd_argon2i' => + 'Missing Crypt::Argon2', } }, ); @@ -548,26 +562,73 @@ DONE $this->assert_str_equals( 'apache-md5', $encoded{$user}->{enc} ); } - $Foswiki::cfg{Htpasswd}{Encoding} = 'bcrypt'; - $impl = new Foswiki::Users::HtPasswdUser( $this->{session} ); + if ( + $this->check_conditions_met( + ( with_dep => 'Crypt::Eksblowfish::Bcrypt' ) + ) + ) + { + $Foswiki::cfg{Htpasswd}{Encoding} = 'bcrypt'; + $Foswiki::cfg{Htpasswd}{BCryptCost} = 3; + $impl = new Foswiki::Users::HtPasswdUser( $this->{session} ); - # force-change them to users2 password again, migrating to bcrypt. - foreach my $user (@users) { - my $added = $impl->setPassword( - $user, - $this->{users2}->{$user}->{pass}, - $this->{users2}->{$user}->{pass} - ); - $this->assert_null( $impl->error() ); - $this->assert_str_not_equals( $encrapted{$user}, - $impl->fetchPass($user) ); - $this->assert_null( $impl->error() ); - $this->assert_str_equals( - $this->{users1}->{$user}->{emails}, - join( ";", $impl->getEmails($user) ) - ); - ( $encrapted{$user}, $encoded{$user} ) = $impl->fetchPass($user); - $this->assert_str_equals( 'bcrypt', $encoded{$user}->{enc} ); + # force-change them to users2 password again, migrating to bcrypt. + foreach my $user (@users) { + my $added = $impl->setPassword( + $user, + $this->{users2}->{$user}->{pass}, + $this->{users2}->{$user}->{pass} + ); + $this->assert_null( $impl->error() ); + $this->assert_str_not_equals( $encrapted{$user}, + $impl->fetchPass($user) ); + $this->assert_null( $impl->error() ); + $this->assert_str_equals( + $this->{users1}->{$user}->{emails}, + join( ";", $impl->getEmails($user) ) + ); + ( $encrapted{$user}, $encoded{$user} ) = $impl->fetchPass($user); + $this->assert_str_equals( 'bcrypt', $encoded{$user}->{enc} ); + $this->assert_matches( qr'\$2a\$03\$', $encrapted{$user}, + "bcrypt settings do not match" ); + } + } + else { + print STDERR +"SKIPPING bcrypt auto recognition, Crypt::Eksblowfish::Bcrypt is not installed.\n"; + } + + if ( $this->check_conditions_met( ( with_dep => 'Crypt::Argon2' ) ) ) { + $Foswiki::cfg{Htpasswd}{Encoding} = 'argon2i'; + $Foswiki::cfg{Htpasswd}{Argon2Memcost} = '16M'; + $Foswiki::cfg{Htpasswd}{Argon2Threads} = 1; + $Foswiki::cfg{Htpasswd}{Argon2Timecost} = 2; + $impl = new Foswiki::Users::HtPasswdUser( $this->{session} ); + + # force-change them to users2 password again, migrating to bcrypt. + foreach my $user (@users) { + my $added = $impl->setPassword( + $user, + $this->{users2}->{$user}->{pass}, + $this->{users2}->{$user}->{pass} + ); + $this->assert_null( $impl->error() ); + $this->assert_str_not_equals( $encrapted{$user}, + $impl->fetchPass($user) ); + $this->assert_null( $impl->error() ); + $this->assert_str_equals( + $this->{users1}->{$user}->{emails}, + join( ";", $impl->getEmails($user) ) + ); + ( $encrapted{$user}, $encoded{$user} ) = $impl->fetchPass($user); + $this->assert_str_equals( 'argon2i', $encoded{$user}->{enc} ); + $this->assert_matches( qr'\$m=16384,t=2,p=1\$', $encrapted{$user}, + "Argon2i settings do not match" ); + } + } + else { + print STDERR +"SKIPPING argon2i auto recognition, Crypt::Argon2 is not installed.\n"; } #dumpFile(); @@ -614,6 +675,22 @@ sub test_htpasswd_bcrypt { #dumpFile(); } +sub test_htpasswd_argon2i { + my $this = shift; + + $Foswiki::cfg{Htpasswd}{AutoDetect} = 0; + $Foswiki::cfg{Htpasswd}{Encoding} = 'argon2i'; + $Foswiki::cfg{Htpasswd}{Argon2Memcost} = '16M'; + $Foswiki::cfg{Htpasswd}{Argon2Threads} = 2; + $Foswiki::cfg{Htpasswd}{Argon2Timecost} = 2; + my $impl = new Foswiki::Users::HtPasswdUser( $this->{session} ); + $this->assert($impl); + $impl->ClearCache() if $impl->can('ClearCache'); + $this->doTests( $impl, $SALTED ); + + #dumpFile(); +} + sub test_htpasswd_crypt_crypt { my $this = shift; $Foswiki::cfg{Htpasswd}{AutoDetect} = 0; diff --git a/core/data/System/ReleaseNotes02x02.txt b/core/data/System/ReleaseNotes02x02.txt index 0500054b2d..2c30493d5d 100644 --- a/core/data/System/ReleaseNotes02x02.txt +++ b/core/data/System/ReleaseNotes02x02.txt @@ -1,4 +1,4 @@ -%META:TOPICINFO{author="ProjectContributor" date="1494776564" format="1.1" version="1"}% +%META:TOPICINFO{author="ProjectContributor" date="1512691253" format="1.1" version="1"}% %META:TOPICPARENT{name="ReleaseHistory"}% ---+!! Foswiki Release 2.2.0 @@ -90,6 +90,26 @@ See [[%BUGS%/Item13696][Item13696]] for up-to-date details. ---+++ Security issues addressed in this release. +---+++ Changes in =htpasswd= support + +The =Argon2i= hashing algorithm is available if the CPAN:Crypt::Argon2 module is installed. This algorithm was the winner of Password Hashing Competition in July 2015. +=Argon2i= has 3 associated tuning parameters, Time cost, Memory cost, and parallelism (threads). They can be adjusted for a reasonable operation time on the deployed hardware. +=Argon2= is not compatible with Apache login. It can only be used with Template login. + +The =bcrypt= algorithm now has limited compatibility with Apache's BCrypt implementation. Apache uses the ==$2y$== bcrypt type, signifying it does not +have a bug in the algorithm that was introduced in the =PHP= implementation. The Perl module CPAN:Crypt::EKSBlowfish::BCrypt does not have that bug and is +does not recognized the =$2y= indicator. + * Password hashes generated by Foswiki will work with Apache login without changes. + * Password entries generated by the Apache =htpasswd= tool must be edited to work with Foswiki Login. Change the =$2y$= string to =$2a= in each hash entry. + +---+++ General note on password security + +No password is secure if the login is done over the HTTP protocol without SSL encryption. Use HTTPS / SSL for best security. The choice of a hashing +algorithm is for the protection of users should your =.htpasswd= file be compromised. Choices like argon2 and bcrypt have a much greater resistance +to various cracking tools. + +Note that the =bcrypt= algorithm truncates passwords at 72 bytes. For this reason, =argon2= is preferred if users use long passphrases. + ---+++ The default Attachment Link formats have changed. The attachment link format has been changed to be more flexible. If you are using the default link formats, no changes are needed. If you have diff --git a/core/lib/Foswiki.spec b/core/lib/Foswiki.spec index 9556a41067..4df7abae9b 100644 --- a/core/lib/Foswiki.spec +++ b/core/lib/Foswiki.spec @@ -700,19 +700,30 @@ $Foswiki::cfg{Htpasswd}{GlobalCache} = $FALSE; # if Foswiki is running in a =mod_perl= or =fcgi= environment. $Foswiki::cfg{Htpasswd}{DetectModification} = $FALSE; -# **SELECT bcrypt,'htdigest-md5','apache-md5',sha1,'crypt-md5',crypt,plain LABEL="Password Encoding" DISPLAY_IF="/htpasswd/i.test({PasswordManager})" CHECK="iff:'{PasswordManager}=~/htpasswd/i'"** -# Password encryption, for the =Foswiki::Users::HtPasswdUser= password +# **SELECT argon2,bcrypt,'htdigest-md5','apache-md5',sha1,'crypt-md5',crypt,plain LABEL="Password Encoding" DISPLAY_IF="/htpasswd/i.test({PasswordManager})" CHECK="iff:'{PasswordManager}=~/htpasswd/i'"** +# Password hashing, for the =Foswiki::Users::HtPasswdUser= password # manager. This specifies the type of password hash to generate when # writing entries to =.htpasswd=. It is also used when reading password # entries unless {Htpasswd}{AutoDetect} is enabled. -# +# +# *No password is secure unless https: is in use* +# # The choices in order of strongest to lowest strength: # * =bcrypt= - Hash based upon blowfish algorithm, strength of hash -# controlled by a cost parameter. +# controlled by a cost parameter. *Caution:* bcrypt has a maximum +# password length of 72 bytes. Passwords longer than 72 will be +# truncated and will generate identical hashes. +# See [[System.ReleaseNotes02x02]] for details on Apache compatibility. +# * =argon2i= - Hash based upon the Argon2, the 2015 Password hash competition winner. +# Argon2 is tunable by specifying the cpu cost, memory cost and parallelism (threads). +# Argon2 would be considered stronger than bcrypt, but it is relatively new and not +# yet completely proven. # *Not compatible with Apache Authentication* -# * =htdigest-md5= - Strongest only when combined with the -# =Foswiki::LoginManager::ApacheLogin=. Useful on sites where -# password files are required to be portable. The {AuthRealm} +# * =htdigest-md5= - Recommended only when combined with the +# =Foswiki::LoginManager::ApacheLogin=, or required for portability. +# Digest authentication provides some basic protection for non-SSL +# (http://) sites. The password is protected with +# simple encryption during browser authentication. The {AuthRealm} # value is used with the username and password to generate the # hashed form of the password, thus: =user:{AuthRealm}:hash=. # This encoding is generated by the Apache =htdigest= command. @@ -721,21 +732,21 @@ $Foswiki::cfg{Htpasswd}{DetectModification} = $FALSE; # 32-bit salt and the password (=userid:$apr1$salt$hash=). # This is the default, and is the encoding generated by the # =htpasswd -m= command. -# * =sha1= - has the strongest hash, however does not use a salt -# and is therefore more vulnerable to dictionary attacks. This +# * =sha1= does not use a salt +# and is therefore highly vulnerable to dictionary attacks. This # is the encoding generated by the =htpasswd -s= command # (=userid:{SHA}hash=). # * =crypt-md5= - Enable use of standard libc (/etc/shadow) # crypt-md5 password (like =user:$1$salt$hash:email=). Unlike # =crypt= encoding, it does not suffer from password truncation. -# Passwords are salted, and the salt is stored in the encrypted +# Passwords are salted, and the salt is stored in the hashed # password string as in normal crypt passwords. This encoding is # understood by Apache but cannot be generated by the =htpasswd= # command. # * =crypt= - encoding uses the first 8 characters of the password. # This is the default generated by the Apache =htpasswd= command # (=user:hash:email=). *Not Recommended.* -# * =plain= - stores passwords as plain text (no encryption). Useful +# * =plain= - stores passwords as plain text (no hashing). Useful # for testing # If you need to create entries in =.htpasswd= before Foswiki is operational, # you can use the =htpasswd= or =htdigest= Apache programs to create a new @@ -771,12 +782,28 @@ $Foswiki::cfg{Htpasswd}{ForceChangeEncoding} = $FALSE; # can require extreme amounts of CPU time. $Foswiki::cfg{Htpasswd}{BCryptCost} = 8; +# **NUMBER LABEL="Argon2 Time Cost" DISPLAY_IF="{PasswordManager}=='Foswiki::Users::HtPasswdUser' && {Htpasswd}{Encoding}=='argon2'" CHECK="min:0 max:99 iff:'{PasswordManager}=~/:HtPasswdUser/ && {Htpasswd}{Encoding} eq q'"** +# Specify the cost (iterations) that should be incurred when computing the hash of a +# password. This number should be increased as CPU speeds increase. +$Foswiki::cfg{Htpasswd}{Argon2Timecost} = 32; + +# **STRING LABEL="Argon2 Memory Cost" DISPLAY_IF="{PasswordManager}=='Foswiki::Users::HtPasswdUser' && {Htpasswd}{Encoding}=='argon2'" CHECK="iff:'{PasswordManager}=~/:HtPasswdUser/ && {Htpasswd}{Encoding} eq q'"** +# Specify the cost in memory that should be incurred when computing the hash of a +# password. Minimum is 64k (or 65536). Can be specified as "k", "M" or "G" for killobytes, Megabytes and Gigabytes respectively. +# (k is lower case, M and G must be uppercase. +$Foswiki::cfg{Htpasswd}{Argon2Memcost} = '16M'; + +# **NUMBER LABEL="Argon2 Parallelism" DISPLAY_IF="{PasswordManager}=='Foswiki::Users::HtPasswdUser' && {Htpasswd}{Encoding}=='argon2'" CHECK="min:1 max:16 iff:'{PasswordManager}=~/:HtPasswdUser/ && {Htpasswd}{Encoding} eq q'"** +# Specify the number of threads that will be required for executing the +# algorithm. +$Foswiki::cfg{Htpasswd}{Argon2Threads} = 4; + # **PASSWORD LABEL="Internal Admin Password" CHECK_ON_CHANGE="{FeatureAccess}{Configure}" CHECK="also:{FeatureAccess}{Configure}" ONSAVE** # If set, this password permits use of the _internal admin_ login, and the # sudo facility. *As it is a "shared password", this is no longer # recommended per good security practices. Clear this field to disable use # of the internal admin login. -# NOTE: this field is encrypted, and the value can only be set using the +# NOTE: this field is hashed, and the value can only be set using the # =configure= interface. $Foswiki::cfg{Password} = ''; diff --git a/core/lib/Foswiki/Configure/Checkers/Htpasswd/Encoding.pm b/core/lib/Foswiki/Configure/Checkers/Htpasswd/Encoding.pm index 8f0e4462d6..4e14074391 100755 --- a/core/lib/Foswiki/Configure/Checkers/Htpasswd/Encoding.pm +++ b/core/lib/Foswiki/Configure/Checkers/Htpasswd/Encoding.pm @@ -38,6 +38,13 @@ my %methods = ( usage => 'use or auto-detection of crypt-md5' } ], + 'argon2i' => [ + { + name => 'Crypt::Argon2', + search => ':$argon2i$', + usage => 'use or auto-detection of argon2' + } + ], 'bcrypt' => [ { name => 'Crypt::Eksblowfish::Bcrypt', diff --git a/core/lib/Foswiki/Users/HtPasswdUser.pm b/core/lib/Foswiki/Users/HtPasswdUser.pm index 1117c7463e..f8b1813ba2 100644 --- a/core/lib/Foswiki/Users/HtPasswdUser.pm +++ b/core/lib/Foswiki/Users/HtPasswdUser.pm @@ -89,6 +89,8 @@ sub new { $this->{APR} = 1 unless ($@); eval 'use Crypt::Eksblowfish::Bcrypt;'; $this->{BCRYPT} = 1 unless ($@); + eval 'use Crypt::Argon2'; + $this->{ARGON2} = 1 unless ($@); } if ( $Foswiki::cfg{Htpasswd}{Encoding} eq 'md5' @@ -123,6 +125,10 @@ sub new { eval 'use Crypt::Eksblowfish::Bcrypt;'; $this->{BCRYPT} = 1 unless ($@); } + elsif ( $Foswiki::cfg{Htpasswd}{Encoding} eq 'argon2i' ) { + eval 'use Crypt::Argon2;'; + $this->{ARGON2} = 1 unless ($@); + } else { print STDERR "ERROR: unknown {Htpasswd}{Encoding} setting : " . $Foswiki::cfg{Htpasswd}{Encoding} . "\n"; @@ -277,7 +283,7 @@ sub _readPasswd { if ( !-e $Foswiki::cfg{Htpasswd}{FileName} ) { print STDERR "WARNING - $Foswiki::cfg{Htpasswd}{FileName} DOES NOT EXIST\n" - if DEBUG; + if TRACE; return $data; } @@ -351,7 +357,10 @@ sub _readPasswd { { $data->{$hID}->{enc} = 'crypt'; } + elsif ( length($tPass) > 60 && $tPass =~ m/^\$argon2i\$/ ) { + $data->{$hID}->{enc} = 'argon2i'; + } elsif ( length($tPass) gt 0 && ( !$fields[0] @@ -644,6 +653,33 @@ sub encrypt { Foswiki::encode_utf8($passwd), Foswiki::encode_utf8($salt) ); } + elsif ( $enc eq 'argon2i' ) { + unless ( $this->{ARGON2} ) { + $this->{error} = "Unsupported Encoding"; + return 0; + } + + my $cost = $Foswiki::cfg{Htpasswd}{Argon2Timecost}; + $cost = 8 unless defined $cost; + + my $threads = $Foswiki::cfg{Htpasswd}{Argon2Threads}; + $threads = 2 unless defined $threads; + + my $mem = $Foswiki::cfg{Htpasswd}{Argon2Memcost}; + $mem = '512k' unless defined $mem; + + my $salt; + $salt = $this->fetchPass($login) unless $fresh; + if ( $fresh || !$salt ) { + $salt = Foswiki::generateRandomChars(16); + } + print STDERR " ARGON2: Cost:$cost Mem:$mem Threads:$threads \n" + if (TRACE); + my $encoded = + Crypt::Argon2::argon2i_pass( $passwd, $salt, $cost, $mem, $threads, + 16 ); + return $encoded if ($encoded); + } die 'Unsupported password encoding ' . $enc; } @@ -716,6 +752,8 @@ sub setPassword { my ( $this, $login, $newUserPassword, $oldUserPassword ) = @_; ASSERT($login) if DEBUG; + $this->{error} = undef; + if ( defined($oldUserPassword) ) { unless ( $oldUserPassword eq '1' ) { return 0 unless $this->checkPassword( $login, $oldUserPassword ); @@ -725,6 +763,10 @@ sub setPassword { $this->{error} = $login . ' already exists'; return 0; } + $this->{error} = + undef; # Clear {error} - fetchPass will set it for missing users! + + my $hashed = $this->encrypt( $login, $newUserPassword, 1 ); my $lockHandle; try { @@ -734,8 +776,8 @@ sub setPassword { # - Don't trust cache my $db = $this->_readPasswd( 0, 1 ); - $db->{$login}->{pass} = $this->encrypt( $login, $newUserPassword, 1 ); - $db->{$login}->{enc} = $Foswiki::cfg{Htpasswd}{Encoding}; + $db->{$login}->{pass} = $hashed; + $db->{$login}->{enc} = $Foswiki::cfg{Htpasswd}{Encoding}; $db->{$login}->{realm} = ( $Foswiki::cfg{Htpasswd}{Encoding} eq 'md5' || $Foswiki::cfg{Htpasswd}{Encoding} eq 'htdigest-md5' ) @@ -760,7 +802,7 @@ sub setPassword { _unlockPasswdFile($lockHandle) if $lockHandle; }; - $this->{error} = undef; + return 0 if ( $this->{error} ); return 1; } @@ -830,6 +872,19 @@ sub checkPassword { # $pw will be 0 if there is no pw return 0 unless defined $pw && length($pw); + if ( $entry->{enc} eq 'argon2i' ) { + if ( $this->{ARGON2} ) { + $this->{error} = ''; + return Crypt::Argon2::argon2i_verify( $pw, $password ); + } + else { + $this->{error} = + 'Internal error - Argon2 password routines not installed'; + Foswiki::Func::writeWarning( $this->{error} ); + return 0; + } + } + my $encryptedPassword = $this->encrypt( $login, $password, 0, $entry ); return 0 unless ($encryptedPassword);