Passed
Pull Request — master (#11)
by Alexander
01:21
created

PasswordHasher::hash()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 13
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 3.2098

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 3
eloc 6
c 2
b 0
f 0
nc 3
nop 1
dl 0
loc 13
ccs 5
cts 7
cp 0.7143
crap 3.2098
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Security;
6
7
/**
8
 * PasswordHasher allows generating password hash and verifying passwords against a hash.
9
 */
10
final class PasswordHasher
11
{
12
    private ?string $algorithm;
13
    private array $parameters;
14
15
    private const SAFE_PARAMETERS = [
16
        PASSWORD_BCRYPT => [
17
            'cost' => 13,
18
        ],
19
    ];
20
21
    /**
22
     * @param string|null $algorithm Algorithm to use. If not specified, PHP chooses safest algorithm available in the
23
     * current version of PHP.
24
     * @param array|null $parameters Algorithm parameters. If not specified, safe defaults are used.
25
     * @see https://www.php.net/manual/en/function.password-hash.php
26
     */
27 4
    public function __construct(?string $algorithm = PASSWORD_DEFAULT, array $parameters = null)
28
    {
29 4
        $this->algorithm = $algorithm;
30
31 4
        if ($parameters === null) {
32 3
            $this->parameters = self::SAFE_PARAMETERS[$algorithm] ?? [];
33
        } else {
34 1
            $this->parameters = $parameters;
35
        }
36 4
    }
37
38
    /**
39
     * Generates a secure hash from a password and a random salt.
40
     *
41
     * The generated hash can be stored in database.
42
     * Later when a password needs to be validated, the hash can be fetched and passed
43
     * to {@see validate()}. For example,
44
     *
45
     * ```php
46
     * // generates the hash (usually done during user registration or when the password is changed)
47
     * $hash = (new PasswordHasher())->hash($password);
48
     * // ...save $hash in database...
49
     *
50
     * // during login, validate if the password entered is correct using $hash fetched from database
51
     * if ((new PasswordHasher())->validate($password, $hash)) {
52
     *     // password is good
53
     * } else {
54
     *     // password is bad
55
     * }
56
     * ```
57
     *
58
     * @param string $password The password to be hashed.
59
     * @return string The password hash string. The output length might increase
60
     * in future versions of PHP (http://php.net/manual/en/function.password-hash.php)
61
     * @see validate()
62
     */
63 2
    public function hash(string $password): string
64
    {
65 2
        $result = password_hash($password, $this->algorithm, $this->parameters);
0 ignored issues
show
Bug introduced by
It seems like $this->algorithm can also be of type string; however, parameter $algo of password_hash() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

65
        $result = password_hash($password, /** @scrutinizer ignore-type */ $this->algorithm, $this->parameters);
Loading history...
66
67 2
        if ($result === null) {
68
            throw new \InvalidArgumentException('Algorithm "' . (string)$this->algorithm . '" is invalid.');
69
        }
70
71 2
        if ($result === false) {
72
            throw new \RuntimeException('Failed to hash password.');
73
        }
74
75 2
        return $result;
76
    }
77
78
    /**
79
     * Verifies a password against a hash.
80
     * @param string $password The password to verify.
81
     * @param string $hash The hash to verify the password against.
82
     * @return bool whether the password is correct.
83
     * @throws \InvalidArgumentException on bad password/hash parameters or if crypt() with Blowfish hash is not
84
     * available.
85
     * @see hash()
86
     */
87 3
    public function validate(string $password, string $hash): bool
88
    {
89 3
        if ($password === '') {
90 1
            throw new \InvalidArgumentException('Password must be a string and cannot be empty.');
91
        }
92
93 2
        return password_verify($password, $hash);
94
    }
95
}
96