Allow adjusting the '$cost' parameter for BCrypt hashes
authorTim Düsterhus <duesterhus@woltlab.com>
Wed, 12 May 2021 10:25:42 +0000 (12:25 +0200)
committerTim Düsterhus <duesterhus@woltlab.com>
Wed, 12 May 2021 10:29:16 +0000 (12:29 +0200)
wcfsetup/install/files/lib/system/user/authentication/password/algorithm/Bcrypt.class.php

index 403a49f05a9a2b2eeeb425685d0ef76ccc056d4b..0fa4a86db12cbc41b83da0e0e03ec3e2bbb9aeb7 100644 (file)
@@ -15,9 +15,31 @@ use wcf\system\user\authentication\password\IPasswordAlgorithm;
  */
 final class Bcrypt implements IPasswordAlgorithm
 {
-    private const OPTIONS = [
-        'cost' => 12,
-    ];
+    /**
+     * @var int
+     */
+    private $cost;
+
+    /**
+     * @param int $cost The BCrypt 'cost' option for newly created hashes. It is recommended not to change this option.
+     */
+    public function __construct(int $cost = 12)
+    {
+        if ($cost < 9) {
+            throw new \InvalidArgumentException(\sprintf(
+                "Refusing to accept BCrypt costs lower than '9', '%d' given.",
+                $cost
+            ));
+        }
+        if ($cost > 14) {
+            throw new \InvalidArgumentException(\sprintf(
+                "Refusing to accept BCrypt costs higher than '14', '%d' given.",
+                $cost
+            ));
+        }
+
+        $this->cost = $cost;
+    }
 
     /**
      * @inheritDoc
@@ -32,7 +54,7 @@ final class Bcrypt implements IPasswordAlgorithm
      */
     public function hash(string $password): string
     {
-        return \password_hash($password, \PASSWORD_BCRYPT, self::OPTIONS);
+        return \password_hash($password, \PASSWORD_BCRYPT, $this->getOptions());
     }
 
     /**
@@ -40,6 +62,16 @@ final class Bcrypt implements IPasswordAlgorithm
      */
     public function needsRehash(string $hash): bool
     {
-        return \password_needs_rehash($hash, \PASSWORD_BCRYPT, self::OPTIONS);
+        return \password_needs_rehash($hash, \PASSWORD_BCRYPT, $this->getOptions());
+    }
+
+    /**
+     * Returns the value to be used for password_*'s `$options` parameter.
+     */
+    private function getOptions()
+    {
+        return [
+            'cost' => $this->cost,
+        ];
     }
 }