Test Failed
Pull Request — main (#59)
by Dimitri
06:14
created

ArgonHandler::threads()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 3
c 1
b 0
f 0
nc 2
nop 1
dl 0
loc 7
ccs 0
cts 2
cp 0
crap 12
rs 10
1
<?php
2
3
/**
4
 * This file is part of Blitz PHP framework.
5
 *
6
 * (c) 2022 Dimitri Sitchet Tomkeu <[email protected]>
7
 *
8
 * For the full copyright and license information, please view
9
 * the LICENSE file that was distributed with this source code.
10
 */
11
12
namespace BlitzPHP\Security\Hashing\Handlers;
13
14
use BlitzPHP\Contracts\Security\HasherInterface;
15
use Error;
16
use RuntimeException;
17
18
/**
19
 * Gestionnaire de hashage basé sur Argon
20
 *
21
 * @credit <a href="http://www.laravel.com">Laravel 11 - \Illuminate\Hashing\ArgonHasher</a>
22
 */
23
class ArgonHandler extends BaseHandler implements HasherInterface
24
{
25
    /**
26
     * Le facteur de coût de la mémoire par défaut.
27
     */
28
    protected int $memory = 1024;
29
30
    /**
31
     * Le facteur de coût du temps par défaut.
32
     */
33
    protected int $time = 2;
34
35
    /**
36
     * Le facteur de filetage par défaut.
37
     */
38
    protected int $threads = 2;
39
40
    /**
41
     * Indique s'il faut effectuer une vérification de l'algorithme.
42
     */
43
    protected bool $verifyAlgorithm = false;
44
45
    /**
46
     * Créer une nouvelle instance de Hacheur.
47
     */
48
    public function __construct(array $options = [])
49
    {
50
        $this->time            = $options['time'] ?? $this->time;
51
        $this->memory          = $options['memory'] ?? $this->memory;
52
        $this->threads         = $this->threads($options);
53
        $this->verifyAlgorithm = $options['verify'] ?? $this->verifyAlgorithm;
54
    }
55
56
    /**
57
     * {@inheritDoc}
58
     *
59
     * @throws RuntimeException
60
     */
61
    public function make(string $value, array $options = []): string
62
    {
63
        try {
64
            $hash = password_hash($value, $this->algorithm(), [
65
                'memory_cost' => $this->memory($options),
66
                'time_cost'   => $this->time($options),
67
                'threads'     => $this->threads($options),
68
            ]);
69
        } catch (Error) {
70
            throw new RuntimeException("Le hachage Argon2 n'est pas supporté.");
71
        }
72
73
        return $hash;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $hash could return the type null which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
74
    }
75
76
    /**
77
     * Obtient l'algorithme à utiliser pour le hachage.
78
     */
79
    protected function algorithm(): string
80
    {
81
        return PASSWORD_ARGON2I;
82
    }
83
84
    /**
85
     * {@inheritDoc}
86
     *
87
     * @throws RuntimeException
88
     */
89
    public function check(string $value, string $hashedValue, array $options = []): bool
90
    {
91
        if ($hashedValue === '') {
92
            return false;
93
        }
94
95
        if ($this->verifyAlgorithm && ! $this->isUsingCorrectAlgorithm($hashedValue)) {
96
            throw new RuntimeException("Ce mot de passe n'utilise pas l'algorithme Argon2i.");
97
        }
98
99
        return parent::check($value, $hashedValue, $options);
100
    }
101
102
    /**
103
     * {@inheritDoc}
104
     */
105
    public function needsRehash(string $hashedValue, array $options = []): bool
106
    {
107
        return password_needs_rehash($hashedValue, $this->algorithm(), [
108
            'memory_cost' => $this->memory($options),
109
            'time_cost'   => $this->time($options),
110
            'threads'     => $this->threads($options),
111
        ]);
112
    }
113
114
    /**
115
     * Vérifie que la configuration est inférieure ou égale à ce qui est configuré.
116
     *
117
     * @internal
118
     */
119
    public function verifyConfiguration(string $value): bool
120
    {
121
        return $this->isUsingCorrectAlgorithm($value) && $this->isUsingValidOptions($value);
122
    }
123
124
    /**
125
     * Vérifie l'algorithme de la valeur hachée.
126
     */
127
    protected function isUsingCorrectAlgorithm(string $hashedValue): bool
128
    {
129
        return $this->info($hashedValue)['algoName'] === 'argon2i';
130
    }
131
132
    /**
133
     * Vérifie les options de la valeur hachée.
134
     */
135
    protected function isUsingValidOptions(string $hashedValue): bool
136
    {
137
        ['options' => $options] = $this->info($hashedValue);
138
139
        if (
140
            ! is_int($options['memory_cost'] ?? null)
141
            || ! is_int($options['time_cost'] ?? null)
142
            || ! is_int($options['threads'] ?? null)
143
        ) {
144
            return false;
145
        }
146
147
        return ! (
148
            $options['memory_cost'] > $this->memory
149
            || $options['time_cost'] > $this->time
150
            || $options['threads'] > $this->threads
151
        );
152
    }
153
154
    /**
155
     * Définit le facteur de mémoire du mot de passe par défaut.
156
     */
157
    public function setMemory(int $memory): self
158
    {
159
        $this->memory = $memory;
160
161
        return $this;
162
    }
163
164
    /**
165
     * Définit le facteur de synchronisation du mot de passe par défaut.
166
     */
167
    public function setTime(int $time): self
168
    {
169
        $this->time = $time;
170
171
        return $this;
172
    }
173
174
    /**
175
     * Définit le facteur de filtrage du mot de passe par défaut.
176
     */
177
    public function setThreads(int $threads): self
178
    {
179
        $this->threads = $threads;
180
181
        return $this;
182
    }
183
184
    /**
185
     * Extrait la valeur du coût de la mémoire du tableau d'options.
186
     */
187
    protected function memory(array $options): int
188
    {
189
        return $options['memory'] ?? $this->memory;
190
    }
191
192
    /**
193
     * Extrait la valeur du coût du temps du tableau des options.
194
     */
195
    protected function time(array $options): int
196
    {
197
        return $options['time'] ?? $this->time;
198
    }
199
200
    /**
201
     * Extrait la valeur du facteur de filtrage du tableau d'options.
202
     */
203
    protected function threads(array $options): int
204
    {
205
        if (defined('PASSWORD_ARGON2_PROVIDER') && PASSWORD_ARGON2_PROVIDER === 'sodium') {
206
            return 1;
207
        }
208
209
        return $options['threads'] ?? $this->threads;
210
    }
211
}
212