* @file
*/
+use Wikimedia\Assert\Assert;
+
/**
* Represents a password hash for use in authentication
*
*/
protected $config;
+ /**
+ * Hash must fit in user_password, which is a tinyblob
+ */
+ const MAX_HASH_SIZE = 255;
+
/**
* Construct the Password object using a string hash
*
*
* @return bool True if needs update, false otherwise
*/
- public function needsUpdate() {
- }
+ abstract public function needsUpdate();
/**
* Compare one Password object to this object
* Password::toString() for each object. This can be overridden to do
* custom comparison, but it is not recommended unless necessary.
*
+ * @deprecated since 1.33, use verify()
+ *
* @param Password|string $other The other password
* @return bool True if equal, false otherwise
*/
public function equals( $other ) {
- if ( !$other instanceof self ) {
- // No need to use the factory because we're definitely making
- // an object of the same type.
- $obj = clone $this;
- $obj->crypt( $other );
- $other = $obj;
+ if ( is_string( $other ) ) {
+ return $this->verify( $other );
}
return hash_equals( $this->toString(), $other->toString() );
}
+ /**
+ * Checks whether the given password matches the hash stored in this object.
+ *
+ * @param string $password Password to check
+ * @return bool
+ */
+ public function verify( $password ) {
+ Assert::parameter( is_string( $password ),
+ '$password', 'must be string, actual: ' . gettype( $password )
+ );
+
+ // No need to use the factory because we're definitely making
+ // an object of the same type.
+ $obj = clone $this;
+ $obj->crypt( $password );
+
+ return hash_equals( $this->toString(), $obj->toString() );
+ }
+
/**
* Convert this hash to a string that can be stored in the database
*
* are considered equivalent.
*
* @return string
+ * @throws PasswordError if password cannot be serialized to fit a tinyblob.
*/
public function toString() {
- return ':' . $this->config['type'] . ':' . $this->hash;
+ $result = ':' . $this->config['type'] . ':' . $this->hash;
+ $this->assertIsSafeSize( $result );
+ return $result;
+ }
+
+ /**
+ * Assert that hash will fit in a tinyblob field.
+ *
+ * This prevents MW from inserting it into the DB
+ * and having MySQL silently truncating it, locking
+ * the user out of their account.
+ *
+ * @param string $hash The hash in question.
+ * @throws PasswordError If hash does not fit in DB.
+ */
+ final protected function assertIsSafeSize( $hash ) {
+ if ( strlen( $hash ) > self::MAX_HASH_SIZE ) {
+ throw new PasswordError( "Password hash is too big" );
+ }
}
/**