null, 'time_cost' => null, 'threads' => null, ]; /** * @inheritDoc */ protected function isSupported() { // It is actually possible to have a PHP build with Argon2i but not Argon2id return defined( 'PASSWORD_ARGON2I' ) || defined( 'PASSWORD_ARGON2ID' ); } /** * @return mixed[] Array of 2nd and third parmeters to password_hash() */ private function prepareParams() { switch ( $this->config['algo'] ) { case 'argon2i': $algo = PASSWORD_ARGON2I; break; case 'argon2id': $algo = PASSWORD_ARGON2ID; break; case 'auto': $algo = defined( 'PASSWORD_ARGON2ID' ) ? PASSWORD_ARGON2ID : PASSWORD_ARGON2I; break; default: throw new LogicException( "Unexpected algo: {$this->config['algo']}" ); } $params = array_intersect_key( $this->config, self::$knownOptions ); return [ $algo, $params ]; } /** * @inheritDoc */ public function crypt( $password ) { list( $algo, $params ) = $this->prepareParams(); $this->hash = password_hash( $password, $algo, $params ); } /** * @inheritDoc */ public function equals( $other ) { wfDeprecated( __METHOD__, '1.33' ); if ( is_string( $other ) ) { return $this->verify( $other ); } // Argon2 key derivation is not deterministic, can't pass objects to equals() return false; } /** * @inheritDoc */ public function verify( $password ) { Assert::parameterType( 'string', $password, '$password' ); return password_verify( $password, $this->hash ); } /** * @inheritDoc */ public function toString() { $res = ":argon2:{$this->hash}"; $this->assertIsSafeSize( $res ); return $res; } /** * @inheritDoc */ public function needsUpdate() { list( $algo, $params ) = $this->prepareParams(); return password_needs_rehash( $this->hash, $algo, $params ); } }