Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
35 / 35
100.00% covered (success)
100.00%
9 / 9
CRAP
100.00% covered (success)
100.00%
1 / 1
Password
100.00% covered (success)
100.00%
35 / 35
100.00% covered (success)
100.00%
9 / 9
9
100.00% covered (success)
100.00%
1 / 1
 hash
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
1
 needsRehash
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
1
 verify
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setOpsLimit
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getOpsLimit
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setMemLimit
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getMemLimit
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getSodiumOpsLimit
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
1
 getSodiumMemLimit
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
1
1<?php declare(strict_types=1);
2/*
3 * This file is part of Aplus Framework Crypto Library.
4 *
5 * (c) Natan Felles <natanfelles@gmail.com>
6 *
7 * For the full copyright and license information, please view the LICENSE
8 * file that was distributed with this source code.
9 */
10namespace Framework\Crypto;
11
12use InvalidArgumentException;
13use SensitiveParameter;
14use SodiumException;
15
16/**
17 * Class Password.
18 *
19 * @package crypto
20 */
21class Password
22{
23    /**
24     * Used to set operations or memory limit as interactive.
25     * It enables the use of 2 CPU operations or 64 MB RAM.
26     *
27     * @var int
28     */
29    public const LIMIT_INTERACTIVE = 0;
30    /**
31     * Used to set operations or memory limit as moderate.
32     * It enables the use of 3 CPU operations or 256 MB RAM.
33     *
34     * @var int
35     */
36    public const LIMIT_MODERATE = 1;
37    /**
38     * Used to set operations or memory limit as sensitive.
39     * It enables the use of 4 CPU operations or 1 GB RAM.
40     *
41     * @var int
42     */
43    public const LIMIT_SENSITIVE = 2;
44    protected static int $opsLimit = Password::LIMIT_INTERACTIVE;
45    protected static int $memLimit = Password::LIMIT_INTERACTIVE;
46
47    /**
48     * Makes a password hash.
49     *
50     * @param string $password
51     * @param int|null $opslimit A Password constant or null to use the default
52     * set for opslimit
53     * @param int|null $memlimit A Password constant or null to use the default
54     * set for memlimit. Typically, it should be paired with the opslimit value
55     *
56     * @see Password::LIMIT_INTERACTIVE
57     * @see Password::LIMIT_MODERATE
58     * @see Password::LIMIT_SENSITIVE
59     *
60     * @throws SodiumException
61     *
62     * @return string
63     */
64    public static function hash(
65        #[SensitiveParameter] string $password,
66        int $opslimit = null,
67        int $memlimit = null
68    ) : string {
69        $opslimit ??= static::getOpsLimit();
70        $memlimit ??= static::getMemLimit();
71        return \sodium_crypto_pwhash_str(
72            $password,
73            static::getSodiumOpsLimit($opslimit),
74            static::getSodiumMemLimit($memlimit)
75        );
76    }
77
78    /**
79     * Checks if a hash needs to be rehashed based on the ops and mem limits.
80     *
81     * @param string $hash
82     * @param int|null $opslimit A Password constant or null to use the default
83     * set for opslimit
84     * @param int|null $memlimit A Password constant or null to use the default
85     * set for memlimit
86     *
87     * @return bool
88     */
89    public static function needsRehash(
90        #[SensitiveParameter] string $hash,
91        int $opslimit = null,
92        int $memlimit = null
93    ) : bool {
94        $opslimit ??= static::getOpsLimit();
95        $memlimit ??= static::getMemLimit();
96        return \sodium_crypto_pwhash_str_needs_rehash(
97            $hash,
98            static::getSodiumOpsLimit($opslimit),
99            static::getSodiumMemLimit($memlimit)
100        );
101    }
102
103    /**
104     * Verifies a password against a hash.
105     *
106     * @param string $password
107     * @param string $hash
108     *
109     * @throws SodiumException
110     *
111     * @return bool
112     */
113    public static function verify(
114        #[SensitiveParameter] string $password,
115        #[SensitiveParameter] string $hash
116    ) : bool {
117        return \sodium_crypto_pwhash_str_verify($hash, $password);
118    }
119
120    /**
121     * Sets the default Password operations limit.
122     *
123     * @param int $opsLimit A Password constant value
124     *
125     * @see Password::LIMIT_INTERACTIVE
126     * @see Password::LIMIT_MODERATE
127     * @see Password::LIMIT_SENSITIVE
128     */
129    public static function setOpsLimit(int $opsLimit) : void
130    {
131        static::$opsLimit = $opsLimit;
132    }
133
134    /**
135     * Gets the default Password operations limit constant value.
136     *
137     * @return int
138     */
139    public static function getOpsLimit() : int
140    {
141        return static::$opsLimit;
142    }
143
144    /**
145     * Sets the default Password memory limit.
146     *
147     * @param int $memLimit A Password constant value. Typically, it should be
148     * paired with the opslimit value
149     *
150     * @see Password::LIMIT_INTERACTIVE
151     * @see Password::LIMIT_MODERATE
152     * @see Password::LIMIT_SENSITIVE
153     */
154    public static function setMemLimit(int $memLimit) : void
155    {
156        static::$memLimit = $memLimit;
157    }
158
159    /**
160     * Gets the default Password memory limit constant value.
161     *
162     * @return int
163     */
164    public static function getMemLimit() : int
165    {
166        return static::$memLimit;
167    }
168
169    /**
170     * Gets an appropriate sodium operations limit value from a Password constant.
171     *
172     * @param int $constant
173     *
174     * @throws InvalidArgumentException if constant value is invalid
175     *
176     * @return int
177     */
178    protected static function getSodiumOpsLimit(int $constant) : int
179    {
180        return match ($constant) {
181            static::LIMIT_INTERACTIVE => \SODIUM_CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE,
182            static::LIMIT_MODERATE => \SODIUM_CRYPTO_PWHASH_OPSLIMIT_MODERATE,
183            static::LIMIT_SENSITIVE => \SODIUM_CRYPTO_PWHASH_OPSLIMIT_SENSITIVE,
184            default => throw new InvalidArgumentException(
185                'Invalid opslimit value: ' . $constant
186            )
187        };
188    }
189
190    /**
191     * Gets an appropriate sodium memory limit value from a Password constant.
192     *
193     * @param int $constant
194     *
195     * @throws InvalidArgumentException if constant value is invalid
196     *
197     * @return int
198     */
199    protected static function getSodiumMemLimit(int $constant) : int
200    {
201        return match ($constant) {
202            static::LIMIT_INTERACTIVE => \SODIUM_CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE,
203            static::LIMIT_MODERATE => \SODIUM_CRYPTO_PWHASH_MEMLIMIT_MODERATE,
204            static::LIMIT_SENSITIVE => \SODIUM_CRYPTO_PWHASH_MEMLIMIT_SENSITIVE,
205            default => throw new InvalidArgumentException(
206                'Invalid memlimit value: ' . $constant
207            )
208        };
209    }
210}