Pbkdf2PasswordHasher   A
last analyzed

Complexity

Total Complexity 12

Size/Duplication

Total Lines 76
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 12
eloc 21
c 0
b 0
f 0
dl 0
loc 76
rs 10

5 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 9 2
A hash() 0 13 4
A verify() 0 11 4
A needsRehash() 0 3 1
A info() 0 6 1
1
<?php
2
3
/**
4
 * This file is part of web-stack
5
 *
6
 * For the full copyright and license information, please view the LICENSE
7
 * file that was distributed with this source code.
8
 */
9
10
declare(strict_types=1);
11
12
namespace Slick\WebStack\Domain\Security\PasswordHasher\Hasher;
13
14
use Slick\WebStack\Domain\Security\Exception\InvalidPasswordException;
15
use Slick\WebStack\Domain\Security\Exception\LogicException;
16
use Slick\WebStack\Domain\Security\PasswordHasher\LegacyPasswordHasherInterface;
17
use SensitiveParameter;
0 ignored issues
show
Bug introduced by
The type SensitiveParameter was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
18
use function strlen;
19
20
/**
21
 * Pbkdf2PasswordHasher uses the PBKDF2 (Password-Based Key Derivation Function 2).
22
 *
23
 * Providing a high level of Cryptographic security,
24
 *   PBKDF2 is recommended by the National Institute of Standards and Technology (NIST).
25
 *
26
 * But also warrants a warning, using PBKDF2 (with a high number of iterations) slows down the process.
27
 *  PBKDF2 should be used with caution and care.
28
 *
29
 * @package Slick\WebStack\Domain\Security\PasswordHasher\Hasher
30
 */
31
final class Pbkdf2PasswordHasher implements LegacyPasswordHasherInterface
32
{
33
    use CheckPasswordLengthTrait;
34
35
    private int $encodedLength = -1;
36
37
38
    /**
39
     * Creates a Pbkdf2PasswordHasher
40
     *
41
     * @param string $algorithm
42
     * @param int<1, max> $iterations
43
     * @param int<0, max> $length
44
     * @param string|null $salt
45
     */
46
    public function __construct(
47
        private readonly string $algorithm = 'sha512',
48
        private readonly int $iterations = 1000,
49
        private readonly int $length = 40,
50
        private readonly ?string $salt = null,
51
    ) {
52
        try {
53
            $this->encodedLength = strlen($this->hash('', 'salt'));
54
        } catch (\LogicException) {
55
            // ignore unsupported algorithm
56
        }
57
    }
58
59
    /**
60
     * @inheritDoc
61
     */
62
    public function hash(#[SensitiveParameter] string $plainPassword, ?string $salt = null): string
63
    {
64
        $salt = $salt ?: $this->salt;
65
        if ($this->isPasswordTooLong($plainPassword)) {
66
            throw new InvalidPasswordException("Password too long");
67
        }
68
69
        if (!in_array($this->algorithm, hash_algos(), true)) {
70
            throw new LogicException(sprintf('The algorithm "%s" is not supported.', $this->algorithm));
71
        }
72
73
        $digest = hash_pbkdf2($this->algorithm, $plainPassword, $salt ?? '', $this->iterations, $this->length, true);
74
        return base64_encode($digest);
75
    }
76
77
    /**
78
     * @inheritDoc
79
     */
80
    public function verify(
81
        string $hashedPassword,
82
        #[SensitiveParameter] string $plainPassword,
83
        ?string $salt = null
84
    ): bool {
85
        if (\strlen($hashedPassword) !== $this->encodedLength || str_contains($hashedPassword, '$')) {
86
            return false;
87
        }
88
89
        return !$this->isPasswordTooLong($plainPassword)
90
            && hash_equals($hashedPassword, $this->hash($plainPassword, $salt));
91
    }
92
93
    /**
94
     * @inheritDoc
95
     */
96
    public function needsRehash(string $hashedPassword): bool
97
    {
98
        return false;
99
    }
100
101
    public function info(): array
102
    {
103
        return [
104
            'algorithm' => $this->algorithm,
105
            'iterations' => $this->iterations,
106
            'length' => $this->encodedLength
107
        ];
108
    }
109
}
110