Merge "Use strict types in includes/password"
This commit is contained in:
commit
2b3ec971d3
12 changed files with 84 additions and 66 deletions
|
|
@ -1,7 +1,4 @@
|
|||
<?php
|
||||
|
||||
use Wikimedia\Assert\Assert;
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
|
@ -21,6 +18,8 @@ use Wikimedia\Assert\Assert;
|
|||
* @file
|
||||
*/
|
||||
|
||||
declare( strict_types = 1 );
|
||||
|
||||
/**
|
||||
* Implements Argon2, a modern key derivation algorithm designed to resist GPU cracking and
|
||||
* side-channel attacks.
|
||||
|
|
@ -40,7 +39,7 @@ class Argon2Password extends Password {
|
|||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected function isSupported() {
|
||||
protected function isSupported() : bool {
|
||||
// It is actually possible to have a PHP build with Argon2i but not Argon2id
|
||||
return defined( 'PASSWORD_ARGON2I' ) || defined( 'PASSWORD_ARGON2ID' );
|
||||
}
|
||||
|
|
@ -48,7 +47,7 @@ class Argon2Password extends Password {
|
|||
/**
|
||||
* @return mixed[] Array of 2nd and third parmeters to password_hash()
|
||||
*/
|
||||
private function prepareParams() {
|
||||
private function prepareParams() : array {
|
||||
switch ( $this->config['algo'] ) {
|
||||
case 'argon2i':
|
||||
$algo = PASSWORD_ARGON2I;
|
||||
|
|
@ -72,7 +71,7 @@ class Argon2Password extends Password {
|
|||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function crypt( $password ) {
|
||||
public function crypt( string $password ) : void {
|
||||
list( $algo, $params ) = $this->prepareParams();
|
||||
$this->hash = password_hash( $password, $algo, $params );
|
||||
}
|
||||
|
|
@ -80,16 +79,14 @@ class Argon2Password extends Password {
|
|||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function verify( $password ) {
|
||||
Assert::parameterType( 'string', $password, '$password' );
|
||||
|
||||
public function verify( string $password ) : bool {
|
||||
return password_verify( $password, $this->hash );
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function toString() {
|
||||
public function toString() : string {
|
||||
$res = ":argon2:{$this->hash}";
|
||||
$this->assertIsSafeSize( $res );
|
||||
return $res;
|
||||
|
|
@ -98,7 +95,7 @@ class Argon2Password extends Password {
|
|||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function needsUpdate() {
|
||||
public function needsUpdate() : bool {
|
||||
list( $algo, $params ) = $this->prepareParams();
|
||||
return password_needs_rehash( $this->hash, $algo, $params );
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@
|
|||
* @file
|
||||
*/
|
||||
|
||||
declare( strict_types = 1 );
|
||||
|
||||
/**
|
||||
* A Bcrypt-hashed password
|
||||
*
|
||||
|
|
@ -29,17 +31,17 @@
|
|||
* @since 1.24
|
||||
*/
|
||||
class BcryptPassword extends ParameterizedPassword {
|
||||
protected function getDefaultParams() {
|
||||
protected function getDefaultParams() : array {
|
||||
return [
|
||||
'rounds' => $this->config['cost'],
|
||||
];
|
||||
}
|
||||
|
||||
protected function getDelimiter() {
|
||||
protected function getDelimiter() : string {
|
||||
return '$';
|
||||
}
|
||||
|
||||
protected function parseHash( $hash ) {
|
||||
protected function parseHash( ?string $hash ) : void {
|
||||
parent::parseHash( $hash );
|
||||
|
||||
$this->params['rounds'] = (int)$this->params['rounds'];
|
||||
|
|
@ -51,7 +53,7 @@ class BcryptPassword extends ParameterizedPassword {
|
|||
* @throws PasswordError If bcrypt has an unknown error
|
||||
* @throws MWException If bcrypt is not supported by PHP
|
||||
*/
|
||||
public function crypt( $password ) {
|
||||
public function crypt( string $password ) : void {
|
||||
if ( !defined( 'CRYPT_BLOWFISH' ) ) {
|
||||
throw new MWException( 'Bcrypt is not supported.' );
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@
|
|||
* @file
|
||||
*/
|
||||
|
||||
declare( strict_types = 1 );
|
||||
|
||||
/**
|
||||
* Helper class for passwords that use another password hash underneath it
|
||||
* and encrypts that hash with a configured secret.
|
||||
|
|
@ -27,18 +29,18 @@
|
|||
* @since 1.24
|
||||
*/
|
||||
class EncryptedPassword extends ParameterizedPassword {
|
||||
protected function getDelimiter() {
|
||||
protected function getDelimiter() : string {
|
||||
return ':';
|
||||
}
|
||||
|
||||
protected function getDefaultParams() {
|
||||
protected function getDefaultParams() : array {
|
||||
return [
|
||||
'cipher' => $this->config['cipher'],
|
||||
'secret' => count( $this->config['secrets'] ) - 1
|
||||
];
|
||||
}
|
||||
|
||||
public function crypt( $password ) {
|
||||
public function crypt( string $password ) : void {
|
||||
$secret = $this->config['secrets'][$this->params['secret']];
|
||||
|
||||
// Clear error string
|
||||
|
|
@ -77,7 +79,7 @@ class EncryptedPassword extends ParameterizedPassword {
|
|||
* @throws MWException If the configuration is not valid
|
||||
* @return bool True if the password was updated
|
||||
*/
|
||||
public function update() {
|
||||
public function update() : bool {
|
||||
if ( count( $this->args ) != 1 || $this->params == $this->getDefaultParams() ) {
|
||||
// Hash does not need updating
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@
|
|||
* @file
|
||||
*/
|
||||
|
||||
declare( strict_types = 1 );
|
||||
|
||||
/**
|
||||
* Represents an invalid password hash. It is represented as the empty string (i.e.,
|
||||
* a password hash with no type).
|
||||
|
|
@ -30,18 +32,18 @@
|
|||
* @since 1.24
|
||||
*/
|
||||
class InvalidPassword extends Password {
|
||||
public function crypt( $plaintext ) {
|
||||
public function crypt( string $plaintext ) : void {
|
||||
}
|
||||
|
||||
public function toString() {
|
||||
public function toString() : string {
|
||||
return '';
|
||||
}
|
||||
|
||||
public function verify( $password ) {
|
||||
public function verify( string $password ) : bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
public function needsUpdate() {
|
||||
public function needsUpdate() : bool {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@
|
|||
* @file
|
||||
*/
|
||||
|
||||
declare( strict_types = 1 );
|
||||
|
||||
/**
|
||||
* This password hash type layers one or more parameterized password types
|
||||
* on top of each other.
|
||||
|
|
@ -31,11 +33,11 @@
|
|||
* @since 1.24
|
||||
*/
|
||||
class LayeredParameterizedPassword extends ParameterizedPassword {
|
||||
protected function getDelimiter() {
|
||||
protected function getDelimiter() : string {
|
||||
return '!';
|
||||
}
|
||||
|
||||
protected function getDefaultParams() {
|
||||
protected function getDefaultParams() : array {
|
||||
$params = [];
|
||||
|
||||
foreach ( $this->config['types'] as $type ) {
|
||||
|
|
@ -53,7 +55,7 @@ class LayeredParameterizedPassword extends ParameterizedPassword {
|
|||
return $params;
|
||||
}
|
||||
|
||||
public function crypt( $password ) {
|
||||
public function crypt( string $password ) : void {
|
||||
$lastHash = $password;
|
||||
foreach ( $this->config['types'] as $i => $type ) {
|
||||
// Construct pseudo-hash based on params and arguments
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@
|
|||
* @file
|
||||
*/
|
||||
|
||||
declare( strict_types = 1 );
|
||||
|
||||
/**
|
||||
* The old style of MediaWiki password hashing. It involves
|
||||
* running MD5 on the password.
|
||||
|
|
@ -27,15 +29,15 @@
|
|||
* @since 1.24
|
||||
*/
|
||||
class MWOldPassword extends ParameterizedPassword {
|
||||
protected function getDefaultParams() {
|
||||
protected function getDefaultParams() : array {
|
||||
return [];
|
||||
}
|
||||
|
||||
protected function getDelimiter() {
|
||||
protected function getDelimiter() : string {
|
||||
return ':';
|
||||
}
|
||||
|
||||
public function crypt( $plaintext ) {
|
||||
public function crypt( string $plaintext ) : void {
|
||||
if ( count( $this->args ) === 1 ) {
|
||||
// Accept (but do not generate) salted passwords with :A: prefix.
|
||||
// These are actually B-type passwords, but an error in a previous
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@
|
|||
* @file
|
||||
*/
|
||||
|
||||
declare( strict_types = 1 );
|
||||
|
||||
/**
|
||||
* The old style of MediaWiki password hashing, with a salt. It involves
|
||||
* running MD5 on the password, and then running MD5 on the salt concatenated
|
||||
|
|
@ -28,15 +30,15 @@
|
|||
* @since 1.24
|
||||
*/
|
||||
class MWSaltedPassword extends ParameterizedPassword {
|
||||
protected function getDefaultParams() {
|
||||
protected function getDefaultParams() : array {
|
||||
return [];
|
||||
}
|
||||
|
||||
protected function getDelimiter() {
|
||||
protected function getDelimiter() : string {
|
||||
return ':';
|
||||
}
|
||||
|
||||
public function crypt( $plaintext ) {
|
||||
public function crypt( string $plaintext ) : void {
|
||||
if ( count( $this->args ) == 0 ) {
|
||||
$this->args[] = MWCryptRand::generateHex( 8 );
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@
|
|||
* @file
|
||||
*/
|
||||
|
||||
declare( strict_types = 1 );
|
||||
|
||||
/**
|
||||
* Helper class for password hash types that have a delimited set of parameters
|
||||
* inside of the hash.
|
||||
|
|
@ -49,7 +51,10 @@ abstract class ParameterizedPassword extends Password {
|
|||
*/
|
||||
protected $args = [];
|
||||
|
||||
protected function parseHash( $hash ) {
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected function parseHash( ?string $hash ) : void {
|
||||
parent::parseHash( $hash );
|
||||
|
||||
if ( $hash === null ) {
|
||||
|
|
@ -78,11 +83,11 @@ abstract class ParameterizedPassword extends Password {
|
|||
}
|
||||
}
|
||||
|
||||
public function needsUpdate() {
|
||||
public function needsUpdate() : bool {
|
||||
return $this->params !== $this->getDefaultParams();
|
||||
}
|
||||
|
||||
public function toString() {
|
||||
public function toString() : string {
|
||||
$str = ':' . $this->config['type'] . ':';
|
||||
|
||||
if ( count( $this->params ) || count( $this->args ) ) {
|
||||
|
|
@ -100,7 +105,7 @@ abstract class ParameterizedPassword extends Password {
|
|||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract protected function getDelimiter();
|
||||
abstract protected function getDelimiter() : string;
|
||||
|
||||
/**
|
||||
* Return an ordered array of default parameters for this password hash
|
||||
|
|
@ -117,5 +122,5 @@ abstract class ParameterizedPassword extends Password {
|
|||
*
|
||||
* @return array
|
||||
*/
|
||||
abstract protected function getDefaultParams();
|
||||
abstract protected function getDefaultParams() : array;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
* @file
|
||||
*/
|
||||
|
||||
use Wikimedia\Assert\Assert;
|
||||
declare( strict_types = 1 );
|
||||
|
||||
/**
|
||||
* Represents a password hash for use in authentication
|
||||
|
|
@ -66,7 +66,7 @@ abstract class Password {
|
|||
|
||||
/**
|
||||
* String representation of the hash without the type
|
||||
* @var string
|
||||
* @var string|null
|
||||
*/
|
||||
protected $hash;
|
||||
|
||||
|
|
@ -93,7 +93,7 @@ abstract class Password {
|
|||
* @param array $config Array of engine configuration options for hashing
|
||||
* @param string|null $hash The raw hash, including the type
|
||||
*/
|
||||
final public function __construct( PasswordFactory $factory, array $config, $hash = null ) {
|
||||
final public function __construct( PasswordFactory $factory, array $config, string $hash = null ) {
|
||||
if ( !$this->isSupported() ) {
|
||||
throw new Exception( 'PHP support not found for ' . get_class( $this ) );
|
||||
}
|
||||
|
|
@ -117,7 +117,7 @@ abstract class Password {
|
|||
*
|
||||
* @return string Password type
|
||||
*/
|
||||
final public function getType() {
|
||||
final public function getType() : string {
|
||||
return $this->config['type'];
|
||||
}
|
||||
|
||||
|
|
@ -126,7 +126,7 @@ abstract class Password {
|
|||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function isSupported() {
|
||||
protected function isSupported() : bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -134,10 +134,10 @@ abstract class Password {
|
|||
* Perform any parsing necessary on the hash to see if the hash is valid
|
||||
* and/or to perform logic for seeing if the hash needs updating.
|
||||
*
|
||||
* @param string $hash The hash, with the :<TYPE>: prefix stripped
|
||||
* @param string|null $hash The hash, with the :<TYPE>: prefix stripped
|
||||
* @throws PasswordError If there is an error in parsing the hash
|
||||
*/
|
||||
protected function parseHash( $hash ) {
|
||||
protected function parseHash( ?string $hash ) : void {
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -145,7 +145,7 @@ abstract class Password {
|
|||
*
|
||||
* @return bool True if needs update, false otherwise
|
||||
*/
|
||||
abstract public function needsUpdate();
|
||||
abstract public function needsUpdate() : bool;
|
||||
|
||||
/**
|
||||
* Checks whether the given password matches the hash stored in this object.
|
||||
|
|
@ -153,9 +153,7 @@ abstract class Password {
|
|||
* @param string $password Password to check
|
||||
* @return bool
|
||||
*/
|
||||
public function verify( $password ) {
|
||||
Assert::parameterType( 'string', $password, '$password' );
|
||||
|
||||
public function verify( string $password ) : bool {
|
||||
// No need to use the factory because we're definitely making
|
||||
// an object of the same type.
|
||||
$obj = clone $this;
|
||||
|
|
@ -176,7 +174,7 @@ abstract class Password {
|
|||
* @return string
|
||||
* @throws PasswordError if password cannot be serialized to fit a tinyblob.
|
||||
*/
|
||||
public function toString() {
|
||||
public function toString() : string {
|
||||
$result = ':' . $this->config['type'] . ':' . $this->hash;
|
||||
$this->assertIsSafeSize( $result );
|
||||
return $result;
|
||||
|
|
@ -192,7 +190,7 @@ abstract class Password {
|
|||
* @param string $hash The hash in question.
|
||||
* @throws PasswordError If hash does not fit in DB.
|
||||
*/
|
||||
final protected function assertIsSafeSize( $hash ) {
|
||||
final protected function assertIsSafeSize( string $hash ) : void {
|
||||
if ( strlen( $hash ) > self::MAX_HASH_SIZE ) {
|
||||
throw new PasswordError( "Password hash is too big" );
|
||||
}
|
||||
|
|
@ -207,5 +205,5 @@ abstract class Password {
|
|||
* @param string $password Password to hash
|
||||
* @throws PasswordError If an internal error occurs in hashing
|
||||
*/
|
||||
abstract public function crypt( $password );
|
||||
abstract public function crypt( string $password ) : void;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@
|
|||
* @file
|
||||
*/
|
||||
|
||||
declare( strict_types = 1 );
|
||||
|
||||
/**
|
||||
* Show an error when any operation involving passwords fails to run.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@
|
|||
* @file
|
||||
*/
|
||||
|
||||
declare( strict_types = 1 );
|
||||
|
||||
/**
|
||||
* Factory class for creating and checking Password objects
|
||||
*
|
||||
|
|
@ -53,7 +55,7 @@ final class PasswordFactory {
|
|||
* @see PasswordFactory::register
|
||||
* @see PasswordFactory::setDefaultType
|
||||
*/
|
||||
public function __construct( array $config = [], $default = '' ) {
|
||||
public function __construct( array $config = [], string $default = '' ) {
|
||||
foreach ( $config as $type => $options ) {
|
||||
$this->register( $type, $options );
|
||||
}
|
||||
|
|
@ -71,7 +73,7 @@ final class PasswordFactory {
|
|||
* @param array $config Array of configuration options. 'class' is required (the Password
|
||||
* subclass name), everything else is passed to the constructor of that class.
|
||||
*/
|
||||
public function register( $type, array $config ) {
|
||||
public function register( string $type, array $config ) : void {
|
||||
$config['type'] = $type;
|
||||
$this->types[$type] = $config;
|
||||
}
|
||||
|
|
@ -85,7 +87,7 @@ final class PasswordFactory {
|
|||
* @param string $type Password hash type
|
||||
* @throws InvalidArgumentException If the type is not registered
|
||||
*/
|
||||
public function setDefaultType( $type ) {
|
||||
public function setDefaultType( string $type ) : void {
|
||||
if ( !isset( $this->types[$type] ) ) {
|
||||
throw new InvalidArgumentException( "Invalid password type $type." );
|
||||
}
|
||||
|
|
@ -97,7 +99,7 @@ final class PasswordFactory {
|
|||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDefaultType() {
|
||||
public function getDefaultType() : string {
|
||||
return $this->default;
|
||||
}
|
||||
|
||||
|
|
@ -108,7 +110,7 @@ final class PasswordFactory {
|
|||
*
|
||||
* @param Config $config Configuration object to load data from
|
||||
*/
|
||||
public function init( Config $config ) {
|
||||
public function init( Config $config ) : void {
|
||||
foreach ( $config->get( 'PasswordConfig' ) as $type => $options ) {
|
||||
$this->register( $type, $options );
|
||||
}
|
||||
|
|
@ -121,7 +123,7 @@ final class PasswordFactory {
|
|||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getTypes() {
|
||||
public function getTypes() : array {
|
||||
return $this->types;
|
||||
}
|
||||
|
||||
|
|
@ -136,7 +138,7 @@ final class PasswordFactory {
|
|||
* @return Password
|
||||
* @throws PasswordError If hash is invalid or type is not recognized
|
||||
*/
|
||||
public function newFromCiphertext( $hash ) {
|
||||
public function newFromCiphertext( ?string $hash ) : Password {
|
||||
if ( $hash === null || $hash === false || $hash === '' ) {
|
||||
return new InvalidPassword( $this, [ 'type' => '' ], null );
|
||||
} elseif ( $hash[0] !== ':' ) {
|
||||
|
|
@ -160,7 +162,7 @@ final class PasswordFactory {
|
|||
* @return Password
|
||||
* @throws PasswordError If hash is invalid or type is not recognized
|
||||
*/
|
||||
public function newFromType( $type ) {
|
||||
public function newFromType( string $type ) : Password {
|
||||
if ( !isset( $this->types[$type] ) ) {
|
||||
throw new PasswordError( "Unrecognized password hash type $type." );
|
||||
}
|
||||
|
|
@ -180,7 +182,7 @@ final class PasswordFactory {
|
|||
* @param Password|null $existing Optional existing hash to get options from
|
||||
* @return Password
|
||||
*/
|
||||
public function newFromPlaintext( $password, Password $existing = null ) {
|
||||
public function newFromPlaintext( ?string $password, Password $existing = null ) : Password {
|
||||
if ( $password === null ) {
|
||||
return new InvalidPassword( $this, [ 'type' => '' ], null );
|
||||
}
|
||||
|
|
@ -207,7 +209,7 @@ final class PasswordFactory {
|
|||
*
|
||||
* @return bool True if needs update, false otherwise
|
||||
*/
|
||||
public function needsUpdate( Password $password ) {
|
||||
public function needsUpdate( Password $password ) : bool {
|
||||
if ( $password->getType() !== $this->default ) {
|
||||
return true;
|
||||
} else {
|
||||
|
|
@ -221,7 +223,7 @@ final class PasswordFactory {
|
|||
* @param int $minLength Minimum length of password to generate
|
||||
* @return string
|
||||
*/
|
||||
public static function generateRandomPasswordString( $minLength = 10 ) {
|
||||
public static function generateRandomPasswordString( int $minLength = 10 ) : string {
|
||||
// Decide the final password length based on our min password length,
|
||||
// stopping at a minimum of 10 chars.
|
||||
$length = max( 10, $minLength );
|
||||
|
|
@ -237,7 +239,7 @@ final class PasswordFactory {
|
|||
*
|
||||
* @return InvalidPassword
|
||||
*/
|
||||
public static function newInvalidPassword() {
|
||||
public static function newInvalidPassword() : InvalidPassword {
|
||||
static $password = null;
|
||||
|
||||
if ( $password === null ) {
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@
|
|||
* @file
|
||||
*/
|
||||
|
||||
declare( strict_types = 1 );
|
||||
|
||||
/**
|
||||
* A PBKDF2-hashed password
|
||||
*
|
||||
|
|
@ -29,7 +31,7 @@
|
|||
* @since 1.24
|
||||
*/
|
||||
class Pbkdf2Password extends ParameterizedPassword {
|
||||
protected function getDefaultParams() {
|
||||
protected function getDefaultParams() : array {
|
||||
return [
|
||||
'algo' => $this->config['algo'],
|
||||
'rounds' => $this->config['cost'],
|
||||
|
|
@ -37,11 +39,11 @@ class Pbkdf2Password extends ParameterizedPassword {
|
|||
];
|
||||
}
|
||||
|
||||
protected function getDelimiter() {
|
||||
protected function getDelimiter() : string {
|
||||
return ':';
|
||||
}
|
||||
|
||||
public function crypt( $password ) {
|
||||
public function crypt( string $password ) : void {
|
||||
if ( count( $this->args ) == 0 ) {
|
||||
$this->args[] = base64_encode( random_bytes( 16 ) );
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue