wiki.techinc.nl/includes/password/Pbkdf2PasswordUsingOpenSSL.php
Kevin Israel 47241a3520 Use OpenSSL if available for PBKDF2 password hashing
This at least doubles the speed, which would allow the number of
iterations to be doubled and computation of the password hash to
complete in the same amount of time as before, or maybe even a
slight bit less.

The doubling in speed is due to an optimization[1] that so far has not
been accepted into PHP's hash extension.[2] In addition, OpenSSL has
optimized assembly-language hash function implementations for several
common CPU architectures. These provide a further, yet more slight,
performance improvement.

While OpenSSL's PKCS5_PBKDF2_HMAC() is not the fastest implementation
around, using it does not add a new library dependency. And although
better password hashing functions exist, PBKDF2 is still the default
in MediaWiki. For these reasons, I think this change makes sense.

[1]: https://github.com/openssl/openssl/commit/c10e3f0cffb3820d
[2]: https://github.com/php/php-src/issues/9604

Change-Id: I7b06590d4c42581f8749336f9c17777f973a506c
2022-10-04 19:46:14 -04:00

83 lines
2.6 KiB
PHP

<?php
/**
* Implements the Pbkdf2PasswordUsingOpenSSL class for the MediaWiki software.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
* @file
*/
declare( strict_types = 1 );
/**
* A PBKDF2-hashed password, using OpenSSL
*
* @since 1.40
*/
class Pbkdf2PasswordUsingOpenSSL extends AbstractPbkdf2Password {
/**
* @var array<string, string>
*/
private static $digestAlgos;
/**
* List of hash algorithms we support and OpenSSL's names for them.
*
* We include only the algorithms that make sense to support, rather than
* all potentially available algorithms. In particular, we do not include:
*
* - Broken algorithms, such as "md5" and "sha1"
* - Algorithms no longer available by default, such as "whirlpool"
* - Algorithms that perform especially poorly on server CPUs relative
* to other available hardware (as of 2022, this includes "sha3-512";
* see <https://keccak.team/2017/is_sha3_slow.html>)
* - Variants for which there is no reason for use, such as "sha384"
* (a truncated "sha512" that starts with a different initial state)
*
* The array keys should match the algorithm names known to hash_pbkdf2().
*/
private const DIGEST_ALGOS = [
'sha256' => 'sha256',
'sha512' => 'sha512',
];
protected function isSupported(): bool {
return self::canUseOpenSSL();
}
protected function getDigestAlgo( string $algo ): ?string {
if ( !isset( self::$digestAlgos ) ) {
self::$digestAlgos = array_intersect( self::DIGEST_ALGOS, openssl_get_md_methods() );
}
return self::$digestAlgos[$algo] ?? null;
}
protected function pbkdf2(
string $digestAlgo,
string $password,
string $salt,
int $rounds,
int $length
): string {
// Clear error string
while ( openssl_error_string() !== false );
$hash = openssl_pbkdf2( $password, $salt, $length, $rounds, $digestAlgo );
if ( !is_string( $hash ) ) {
throw new PasswordError( 'Error when hashing password: ' . openssl_error_string() );
}
return $hash;
}
}