Merge "Use strict types in includes/password"

This commit is contained in:
jenkins-bot 2019-11-19 19:30:46 +00:00 committed by Gerrit Code Review
commit 2b3ec971d3
12 changed files with 84 additions and 66 deletions

View file

@ -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 );
}

View file

@ -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.' );
}

View file

@ -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;

View file

@ -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;
}
}

View file

@ -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

View file

@ -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

View file

@ -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 );
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -18,6 +18,8 @@
* @file
*/
declare( strict_types = 1 );
/**
* Show an error when any operation involving passwords fails to run.
*

View file

@ -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 ) {

View file

@ -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 ) );
}