Rev 1109 | Rev 1111 | Go to most recent revision | Show entire file | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed
Rev 1109 | Rev 1110 | ||
---|---|---|---|
Line 1... | Line 1... | ||
1 | <?php |
1 | <?php |
2 | 2 | ||
3 | /* |
3 | /* |
4 | * ViaThinkSoft Modular Crypt Format 1.0 and vts_password_*() functions |
4 | * ViaThinkSoft Modular Crypt Format 1.0 and vts_password_*() functions |
5 | * Copyright 2023 Daniel Marschall, ViaThinkSoft |
5 | * Copyright 2023 Daniel Marschall, ViaThinkSoft |
6 | * Revision 2023-03-02 |
6 | * Revision 2023-03-03 |
7 | * |
7 | * |
8 | * Licensed under the Apache License, Version 2.0 (the "License"); |
8 | * Licensed under the Apache License, Version 2.0 (the "License"); |
9 | * you may not use this file except in compliance with the License. |
9 | * you may not use this file except in compliance with the License. |
10 | * You may obtain a copy of the License at |
10 | * You may obtain a copy of the License at |
11 | * |
11 | * |
Line 27... | Line 27... | ||
27 | The function vts_password_verify() replaces password_verify(). |
27 | The function vts_password_verify() replaces password_verify(). |
28 | 28 | ||
29 | ViaThinkSoft Modular Crypt Format 1.0 performs a simple hash or HMAC operation. |
29 | ViaThinkSoft Modular Crypt Format 1.0 performs a simple hash or HMAC operation. |
30 | No key derivation function or iterations are performed. |
30 | No key derivation function or iterations are performed. |
31 | Format: |
31 | Format: |
32 | $1.3.6.1.4.1.37476.3.0.1.1$a=<algo>,m=<mode>$<salt>$<hash> |
32 | $1.3.6.1.4.1.37476.3.0.1.1$a=<algo>,m=<mode>[,i=<iterations>]$<salt>$<hash> |
33 | where <algo> is any valid hash algorithm (name scheme of PHP hash_algos() preferred), e.g. |
33 | where <algo> is any valid hash algorithm (name scheme of PHP hash_algos() preferred), e.g. |
34 | sha3-512 |
34 | sha3-512 |
35 | sha3-384 |
35 | sha3-384 |
36 | sha3-256 |
36 | sha3-256 |
37 | sha3-224 |
37 | sha3-224 |
Line 51... | Line 51... | ||
51 | sp = salt + password |
51 | sp = salt + password |
52 | ps = password + salt |
52 | ps = password + salt |
53 | sps = salt + password + salt |
53 | sps = salt + password + salt |
54 | hmac = HMAC (salt is the key) |
54 | hmac = HMAC (salt is the key) |
55 | pbkdf2 = PBKDF2-HMAC (Additional param i= contains the number of iterations) |
55 | pbkdf2 = PBKDF2-HMAC (Additional param i= contains the number of iterations) |
- | 56 | <iterations> can be omitted if 0. It is required for mode=pbkdf2. For sp/ps/sps/hmac, it is optional. |
|
56 | Like most Crypt-hashes, <salt> and <hash> are Radix64 coded |
57 | Like most Crypt-hashes, <salt> and <hash> are Radix64 coded |
57 | with alphabet './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' and no padding. |
58 | with alphabet './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' and no padding. |
58 | Link to the online specification: |
59 | Link to the online specification: |
59 | https://oidplus.viathinksoft.com/oidplus/?goto=oid%3A1.3.6.1.4.1.37476.3.0.1.1 |
60 | https://oidplus.viathinksoft.com/oidplus/?goto=oid%3A1.3.6.1.4.1.37476.3.0.1.1 |
60 | Reference implementation in PHP: |
61 | Reference implementation in PHP: |
Line 90... | Line 91... | ||
90 | define('PASSWORD_BLOWFISH_DEFAULT_COST', 10); |
91 | define('PASSWORD_BLOWFISH_DEFAULT_COST', 10); |
91 | define('PASSWORD_SHA256_DEFAULT_ROUNDS', 5000); |
92 | define('PASSWORD_SHA256_DEFAULT_ROUNDS', 5000); |
92 | define('PASSWORD_SHA512_DEFAULT_ROUNDS', 5000); |
93 | define('PASSWORD_SHA512_DEFAULT_ROUNDS', 5000); |
93 | define('PASSWORD_VTS_MCF1_DEFAULT_ALGO', 'sha3-512'); // any value in hash_algos(), NOT vts_hash_algos() |
94 | define('PASSWORD_VTS_MCF1_DEFAULT_ALGO', 'sha3-512'); // any value in hash_algos(), NOT vts_hash_algos() |
94 | define('PASSWORD_VTS_MCF1_DEFAULT_MODE', PASSWORD_VTS_MCF1_MODE_PS); |
95 | define('PASSWORD_VTS_MCF1_DEFAULT_MODE', PASSWORD_VTS_MCF1_MODE_PS); |
95 | define('PASSWORD_VTS_MCF1_DEFAULT_ITERATIONS', 0); // only for mode=pbkdf2. 0=Default, depending on algo |
96 | define('PASSWORD_VTS_MCF1_DEFAULT_ITERATIONS', 0); // For PBKDF2, iterations=0 means: Default, depending on the algo |
96 | 97 | ||
97 | // --- Part 1: Modular Crypt Format encode/decode |
98 | // --- Part 1: Modular Crypt Format encode/decode |
98 | 99 | ||
99 | function crypt_modular_format_encode($id, $bin_salt, $bin_hash, $params=null) { |
100 | function crypt_modular_format_encode($id, $bin_salt, $bin_hash, $params=null) { |
100 | // $<id>[$<param>=<value>(,<param>=<value>)*][$<salt>[$<hash>]] |
101 | // $<id>[$<param>=<value>(,<param>=<value>)*][$<salt>[$<hash>]] |
Line 142... | Line 143... | ||
142 | } |
143 | } |
143 | 144 | ||
144 | $dummy = array_shift($ary); |
145 | $dummy = array_shift($ary); |
145 | $bin_hash = crypt_radix64_decode($dummy); |
146 | $bin_hash = crypt_radix64_decode($dummy); |
146 | 147 | ||
- | 148 | return array('id' => $id, |
|
- | 149 | 'salt' => $bin_salt, |
|
- | 150 | 'hash' => $bin_hash, |
|
147 | return array('id' => $id, 'salt' => $bin_salt, 'hash' => $bin_hash, 'params' => $params); |
151 | 'params' => $params); |
148 | } |
152 | } |
149 | 153 | ||
150 | // --- Part 2: ViaThinkSoft Modular Crypt Format 1.0 |
154 | // --- Part 2: ViaThinkSoft Modular Crypt Format 1.0 |
151 | 155 | ||
152 | function vts_crypt_version($hash) { |
156 | function vts_crypt_version($hash) { |
Line 155... | Line 159... | ||
155 | } else { |
159 | } else { |
156 | return '0'; |
160 | return '0'; |
157 | } |
161 | } |
158 | } |
162 | } |
159 | 163 | ||
160 | function _default_iterations($algo, $userland) { |
- | |
161 | if ($userland) { |
- | |
162 | return 100; // because the userland implementation is EXTREMELY slow, we must choose a small value, sorry... |
- | |
163 | } else { |
- | |
164 | // Recommendations taken from https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#pbkdf2 |
- | |
165 | // Note that hash_pbkdf2() implements PBKDF2-HMAC-* |
- | |
166 | if ($algo == 'sha3-512') return 100000; |
- | |
167 | else if ($algo == 'sha3-384') return 100000; |
- | |
168 | else if ($algo == 'sha3-256') return 100000; |
- | |
169 | else if ($algo == 'sha3-224') return 100000; |
- | |
170 | else if ($algo == 'sha512') return 210000; // value by owasp.org cheatcheat (28 February 2023) |
- | |
171 | else if ($algo == 'sha512/256') return 210000; // value by owasp.org cheatcheat (28 February 2023) |
- | |
172 | else if ($algo == 'sha512/224') return 210000; // value by owasp.org cheatcheat (28 February 2023) |
- | |
173 | else if ($algo == 'sha384') return 600000; |
- | |
174 | else if ($algo == 'sha256') return 600000; // value by owasp.org cheatcheat (28 February 2023) |
- | |
175 | else if ($algo == 'sha224') return 600000; |
- | |
176 | else if ($algo == 'sha1') return 1300000; // value by owasp.org cheatcheat (28 February 2023) |
- | |
177 | else if ($algo == 'md5') return 5000000; |
- | |
178 | else return 5000; |
- | |
179 | } |
- | |
180 | } |
- | |
181 | - | ||
182 | function vts_crypt_hash($algo, $str_password, $str_salt, $ver='1', $mode=PASSWORD_VTS_MCF1_DEFAULT_MODE, $iterations=PASSWORD_VTS_MCF1_DEFAULT_ITERATIONS) { |
164 | function vts_crypt_hash($algo, $str_password, $str_salt, $ver='1', $mode=PASSWORD_VTS_MCF1_DEFAULT_MODE, $iterations=PASSWORD_VTS_MCF1_DEFAULT_ITERATIONS) { |
183 | if ($ver == '1') { |
165 | if ($ver == '1') { |
184 | if ($mode == PASSWORD_VTS_MCF1_MODE_SP) { |
166 | if ($mode == PASSWORD_VTS_MCF1_MODE_SP) { |
185 | $payload = $str_salt.$str_password; |
167 | $bin_hash = hash_ex($algo, $str_salt.$str_password, true); |
186 | if (!hash_supported_natively($algo) && str_starts_with($algo, 'sha3-') && method_exists('\bb\Sha3\Sha3', 'hash')) { |
- | |
187 | $bits = explode('-',$algo)[1]; |
168 | for ($i=0; $i<$iterations; $i++) { |
188 | $bin_hash = \bb\Sha3\Sha3::hash($payload, $bits, true); |
- | |
189 | } else { |
- | |
190 | $bin_hash = hash($algo, $payload, true); |
169 | $bin_hash = hash_ex($algo, $str_salt.$bin_hash.$i, true); |
191 | } |
170 | } |
192 | } else if ($mode == PASSWORD_VTS_MCF1_MODE_PS) { |
171 | } else if ($mode == PASSWORD_VTS_MCF1_MODE_PS) { |
193 | $payload = $str_password.$str_salt; |
172 | $bin_hash = hash_ex($algo, $str_password.$str_salt, true); |
194 | if (!hash_supported_natively($algo) && str_starts_with($algo, 'sha3-') && method_exists('\bb\Sha3\Sha3', 'hash')) { |
- | |
195 | $bits = explode('-',$algo)[1]; |
173 | for ($i=0; $i<$iterations; $i++) { |
196 | $bin_hash = \bb\Sha3\Sha3::hash($payload, $bits, true); |
- | |
197 | } else { |
- | |
198 | $bin_hash = hash($algo, $payload, true); |
174 | $bin_hash = hash_ex($algo, $bin_hash.$i.$str_salt, true); |
199 | } |
175 | } |
200 | } else if ($mode == PASSWORD_VTS_MCF1_MODE_SPS) { |
176 | } else if ($mode == PASSWORD_VTS_MCF1_MODE_SPS) { |
201 | $payload = $str_salt.$str_password.$str_salt; |
177 | $bin_hash = hash_ex($algo, $str_salt.$str_password.$str_salt, true); |
202 | if (!hash_supported_natively($algo) && str_starts_with($algo, 'sha3-') && method_exists('\bb\Sha3\Sha3', 'hash')) { |
- | |
203 | $bits = explode('-',$algo)[1]; |
178 | for ($i=0; $i<$iterations; $i++) { |
204 | $bin_hash = \bb\Sha3\Sha3::hash($payload, $bits, true); |
- | |
205 | } else { |
- | |
206 | $bin_hash = hash($algo, $payload, true); |
179 | $bin_hash = hash_ex($algo, $str_salt.$bin_hash.$i.$str_salt, true); |
207 | } |
180 | } |
208 | } else if ($mode == PASSWORD_VTS_MCF1_MODE_HMAC) { |
181 | } else if ($mode == PASSWORD_VTS_MCF1_MODE_HMAC) { |
209 | if (!hash_hmac_supported_natively($algo) && str_starts_with($algo, 'sha3-') && method_exists('\bb\Sha3\Sha3', 'hash_hmac')) { |
182 | $bin_hash = hash_hmac_ex($algo, $str_password, $str_salt, true); |
210 | $bits = explode('-',$algo)[1]; |
183 | for ($i=0; $i<$iterations; $i++) { |
211 | $bin_hash = \bb\Sha3\Sha3::hash_hmac($str_password, $str_salt, $bits, true); |
184 | // https://security.stackexchange.com/questions/149299/rounds-in-a-hashing-function |
212 | } else { |
- | |
213 | $bin_hash = hash_hmac($algo, $str_password, $str_salt, true); |
185 | $bin_hash = hash_hmac_ex($algo, $str_password, $bin_hash.$i, true); |
214 | } |
186 | } |
215 | } else if ($mode == PASSWORD_VTS_MCF1_MODE_PBKDF2) { |
187 | } else if ($mode == PASSWORD_VTS_MCF1_MODE_PBKDF2) { |
216 | if (!hash_pbkdf2_supported_natively($algo) && str_starts_with($algo, 'sha3-') && method_exists('\bb\Sha3\Sha3', 'hash_pbkdf2')) { |
188 | // Note: If $iterations=0, then hash_pbkdf2_ex() will correct it to the best value depending on $algo, see _vts_password_default_iterations(). |
217 | if ($iterations == 0/*default*/) { |
- | |
218 | $iterations = _default_iterations($algo, true); |
- | |
219 | } |
- | |
220 | $bits = explode('-',$algo)[1]; |
- | |
221 | $bin_hash = \bb\Sha3\Sha3::hash_pbkdf2($str_password, $str_salt, $iterations, $bits, 0, true); |
- | |
222 | } else { |
- | |
223 | if ($iterations == 0/*default*/) { |
- | |
224 | $iterations = _default_iterations($algo, false); |
- | |
225 | } |
- | |
226 | $bin_hash = hash_pbkdf2($algo, $str_password, $str_salt, $iterations, 0, true); |
189 | $bin_hash = hash_pbkdf2_ex($algo, $str_password, $str_salt, $iterations, 0, true); |
227 | } |
- | |
228 | } else { |
190 | } else { |
229 | throw new Exception("Invalid VTS crypt version 1 mode. Expect sp, ps, sps, hmac, or pbkdf2."); |
191 | throw new Exception("Invalid VTS crypt version 1 mode. Expect sp, ps, sps, hmac, or pbkdf2."); |
230 | } |
192 | } |
231 | $bin_salt = $str_salt; |
193 | $bin_salt = $str_salt; |
232 | $params = array(); |
194 | $params = array(); |
233 | $params['a'] = $algo; |
195 | $params['a'] = $algo; |
234 | $params['m'] = $mode; |
196 | $params['m'] = $mode; |
235 | if ($mode == PASSWORD_VTS_MCF1_MODE_PBKDF2) $params['i'] = $iterations; |
197 | if ($iterations != 0) $params['i'] = $iterations; // i can be omitted if it is 0. |
236 | return crypt_modular_format_encode(OID_MCF_VTS_V1, $bin_salt, $bin_hash, $params); |
198 | return crypt_modular_format_encode(OID_MCF_VTS_V1, $bin_salt, $bin_hash, $params); |
237 | } else { |
199 | } else { |
238 | throw new Exception("Invalid VTS crypt version, expect 1."); |
200 | throw new Exception("Invalid VTS crypt version, expect 1."); |
239 | } |
201 | } |
240 | } |
202 | } |
Line 258... | Line 220... | ||
258 | 220 | ||
259 | if ($mode == PASSWORD_VTS_MCF1_MODE_PBKDF2) { |
221 | if ($mode == PASSWORD_VTS_MCF1_MODE_PBKDF2) { |
260 | if (!isset($params['i'])) throw new Exception('Param "i" (iterations) missing'); |
222 | if (!isset($params['i'])) throw new Exception('Param "i" (iterations) missing'); |
261 | $iterations = $params['i']; |
223 | $iterations = $params['i']; |
262 | } else { |
224 | } else { |
263 | $iterations = 0; |
225 | $iterations = isset($params['i']) ? $params['i'] : 0; |
264 | } |
226 | } |
265 | 227 | ||
266 | // Create a VTS MCF 1.0 hash based on the parameters of $hash and the password $password |
228 | // Create a VTS MCF 1.0 hash based on the parameters of $hash and the password $password |
267 | $calc_authkey_1 = vts_crypt_hash($algo, $password, $bin_salt, $ver, $mode, $iterations); |
229 | $calc_authkey_1 = vts_crypt_hash($algo, $password, $bin_salt, $ver, $mode, $iterations); |
268 | 230 | ||
Line 303... | Line 265... | ||
303 | function vts_password_get_info($hash) { |
265 | function vts_password_get_info($hash) { |
304 | if (vts_crypt_version($hash) == '1') { |
266 | if (vts_crypt_version($hash) == '1') { |
305 | // OID_MCF_VTS_V1 |
267 | // OID_MCF_VTS_V1 |
306 | $mcf = crypt_modular_format_decode($hash); |
268 | $mcf = crypt_modular_format_decode($hash); |
307 | 269 | ||
308 | //$options['salt_length'] = strlen($mcf['salt']); // Note: salt_length is not a MCF option! It's just a hint for vts_password_hash() |
270 | //$options['salt_length'] = strlen($mcf['salt']); // Note: salt_length is not an MCF option! It's just a hint for vts_password_hash() |
309 | 271 | ||
310 | if (!isset($mcf['params']['a'])) throw new Exception('Param "a" (algo) missing'); |
272 | if (!isset($mcf['params']['a'])) throw new Exception('Param "a" (algo) missing'); |
311 | $options['algo'] = $mcf['params']['a']; |
273 | $options['algo'] = $mcf['params']['a']; |
312 | 274 | ||
313 | if (!isset($mcf['params']['m'])) throw new Exception('Param "m" (mode) missing'); |
275 | if (!isset($mcf['params']['m'])) throw new Exception('Param "m" (mode) missing'); |
314 | $options['mode'] = $mcf['params']['m']; |
276 | $options['mode'] = $mcf['params']['m']; |
315 | 277 | ||
316 | if ($options['mode'] == PASSWORD_VTS_MCF1_MODE_PBKDF2) { |
278 | if ($options['mode'] == PASSWORD_VTS_MCF1_MODE_PBKDF2) { |
317 | if (!isset($mcf['params']['i'])) throw new Exception('Param "i" (iterations) missing'); |
279 | if (!isset($mcf['params']['i'])) throw new Exception('Param "i" (iterations) missing'); |
318 | $options['iterations'] = (int)$mcf['params']['i']; |
280 | $options['iterations'] = (int)$mcf['params']['i']; |
- | 281 | } else { |
|
- | 282 | $options['iterations'] = isset($mcf['params']['i']) ? (int)$mcf['params']['i'] : 0; |
|
319 | } |
283 | } |
320 | 284 | ||
321 | return array( |
285 | return array( |
322 | "algo" => PASSWORD_VTS_MCF1, |
286 | "algo" => PASSWORD_VTS_MCF1, |
323 | "algoName" => "vts-mcf-v1", |
287 | "algoName" => "vts-mcf-v1", |
Line 440... | Line 404... | ||
440 | // Algorithms: PASSWORD_VTS_MCF1 |
404 | // Algorithms: PASSWORD_VTS_MCF1 |
441 | $ver = '1'; |
405 | $ver = '1'; |
442 | $algo = $options['algo']; |
406 | $algo = $options['algo']; |
443 | $mode = $options['mode']; |
407 | $mode = $options['mode']; |
444 | $iterations = $options['iterations']; |
408 | $iterations = $options['iterations']; |
445 | $salt_len = isset($options['salt_length']) ? $options['salt_length'] : 50; // Note: salt_length is not a MCF option! It's just a hint for vts_password_hash() |
409 | $salt_len = isset($options['salt_length']) ? $options['salt_length'] : 32; // Note: salt_length is not an MCF option! It's just a hint for vts_password_hash() |
446 | $salt = random_bytes_ex($salt_len, true, true); |
410 | $salt = random_bytes_ex($salt_len, true, true); |
447 | return vts_crypt_hash($algo, $password, $salt, $ver, $mode, $iterations); |
411 | return vts_crypt_hash($algo, $password, $salt, $ver, $mode, $iterations); |
448 | } else { |
412 | } else { |
449 | // Algorithms: PASSWORD_DEFAULT |
413 | // Algorithms: PASSWORD_DEFAULT |
450 | // PASSWORD_BCRYPT |
414 | // PASSWORD_BCRYPT |
Line 477... | Line 441... | ||
477 | // but it is not a valid option inside the MCF options |
441 | // but it is not a valid option inside the MCF options |
478 | // and it is not a valid option for vts_password_get_info(). |
442 | // and it is not a valid option for vts_password_get_info(). |
479 | unset($options['salt_length']); |
443 | unset($options['salt_length']); |
480 | } |
444 | } |
481 | 445 | ||
482 | // iterations=0 means: Default, depending on the algo |
446 | // For PBKDF2, iterations=0 means: Default, depending on the algo |
483 | if (($options2['mode'] == PASSWORD_VTS_MCF1_MODE_PBKDF2) && ($options['iterations'] == 0/*default*/)) { |
447 | if (($options['iterations'] == 0/*default*/) && ($options2['mode'] == PASSWORD_VTS_MCF1_MODE_PBKDF2)) { |
484 | $algo = $options2['algo']; |
448 | $algo = $options2['algo']; |
485 | $userland = !hash_pbkdf2_supported_natively($algo) && str_starts_with($algo, 'sha3-') && method_exists('\bb\Sha3\Sha3', 'hash_pbkdf2'); |
449 | $userland = !hash_pbkdf2_supported_natively($algo) && str_starts_with($algo, 'sha3-') && method_exists('\bb\Sha3\Sha3', 'hash_pbkdf2'); |
486 | $options['iterations'] = _default_iterations($algo, $userland); |
450 | $options['iterations'] = _vts_password_default_iterations($algo, $userland); |
487 | } |
451 | } |
488 | } |
452 | } |
489 | 453 | ||
490 | // Check if options match |
454 | // Check if options match |
491 | if (count($options) !== count($options2)) return true; |
455 | if (count($options) !== count($options2)) return true; |
Line 508... | Line 472... | ||
508 | // Hash created by vts_password_hash(), password_hash(), or crypt() |
472 | // Hash created by vts_password_hash(), password_hash(), or crypt() |
509 | return password_verify($password, $hash); |
473 | return password_verify($password, $hash); |
510 | } |
474 | } |
511 | } |
475 | } |
512 | 476 | ||
- | 477 | // --- Part 4: Functions which include a fallback to a pure-PHP sha3 implementation (requires https://github.com/danielmarschall/php-sha3 ) |
|
- | 478 | ||
- | 479 | function hash_ex($algo, $data, $binary=false, $options=array()) { |
|
- | 480 | if (!hash_supported_natively($algo) && str_starts_with($algo, 'sha3-') && method_exists('\bb\Sha3\Sha3', 'hash')) { |
|
- | 481 | $bits = explode('-',$algo)[1]; |
|
- | 482 | $hash = \bb\Sha3\Sha3::hash($data, $bits, $binary); |
|
- | 483 | } else { |
|
- | 484 | $hash = hash($algo, $data, $binary); |
|
- | 485 | } |
|
- | 486 | return $hash; |
|
- | 487 | } |
|
- | 488 | ||
- | 489 | function hash_hmac_ex($algo, $data, $key, $binary=false) { |
|
- | 490 | if (!hash_hmac_supported_natively($algo) && str_starts_with($algo, 'sha3-') && method_exists('\bb\Sha3\Sha3', 'hash_hmac')) { |
|
- | 491 | $bits = explode('-',$algo)[1]; |
|
- | 492 | $hash = \bb\Sha3\Sha3::hash_hmac($data, $key, $bits, $binary); |
|
- | 493 | } else { |
|
- | 494 | $hash = hash_hmac($algo, $data, $key, $binary); |
|
- | 495 | } |
|
- | 496 | return $hash; |
|
- | 497 | } |
|
- | 498 | ||
- | 499 | function hash_pbkdf2_ex($algo, $password, $salt, &$iterations=0, $length=0, $binary=false) { |
|
- | 500 | if (!hash_pbkdf2_supported_natively($algo) && str_starts_with($algo, 'sha3-') && method_exists('\bb\Sha3\Sha3', 'hash_pbkdf2')) { |
|
- | 501 | if ($iterations == 0/*default*/) { |
|
- | 502 | $iterations = _vts_password_default_iterations($algo, true); |
|
- | 503 | } |
|
- | 504 | $bits = explode('-',$algo)[1]; |
|
- | 505 | $hash = \bb\Sha3\Sha3::hash_pbkdf2($password, $salt, $iterations, $bits, $length, $binary); |
|
- | 506 | } else { |
|
- | 507 | if ($iterations == 0/*default*/) { |
|
- | 508 | $iterations = _vts_password_default_iterations($algo, false); |
|
- | 509 | } |
|
- | 510 | $hash = hash_pbkdf2($algo, $password, $salt, $iterations, $length, $binary); |
|
- | 511 | } |
|
- | 512 | return $hash; |
|
- | 513 | } |
|
- | 514 | ||
513 | // --- Part 4: Useful functions required by the crypt-functions |
515 | // --- Part 5: Useful functions required by the crypt-functions |
514 | 516 | ||
515 | define('BASE64_RFC4648_ALPHABET', '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/'); |
517 | define('BASE64_RFC4648_ALPHABET', '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/'); |
516 | define('BASE64_CRYPT_ALPHABET', './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'); |
518 | define('BASE64_CRYPT_ALPHABET', './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'); |
517 | 519 | ||
518 | function des_compat_salt($salt_len) { |
520 | function des_compat_salt($salt_len) { |
Line 617... | Line 619... | ||
617 | if ($options['mode'] == PASSWORD_VTS_MCF1_MODE_PBKDF2) { |
619 | if ($options['mode'] == PASSWORD_VTS_MCF1_MODE_PBKDF2) { |
618 | if (!isset($options['iterations'])) { |
620 | if (!isset($options['iterations'])) { |
619 | $options['iterations'] = PASSWORD_VTS_MCF1_DEFAULT_ITERATIONS; |
621 | $options['iterations'] = PASSWORD_VTS_MCF1_DEFAULT_ITERATIONS; |
620 | } |
622 | } |
621 | } else { |
623 | } else { |
622 | unset($options['iterations']); |
624 | $options['iterations'] = isset($options['iterations']) ? $options['iterations'] : 0; |
623 | } |
625 | } |
624 | } |
626 | } |
625 | return $options; |
627 | return $options; |
626 | } |
628 | } |
627 | 629 | ||
- | 630 | function _vts_password_default_iterations($algo, $userland) { |
|
- | 631 | if ($userland) { |
|
- | 632 | return 100; // because the userland implementation is EXTREMELY slow, we must choose a small value, sorry... |
|
- | 633 | } else { |
|
- | 634 | // Recommendations taken from https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#pbkdf2 |
|
- | 635 | // Note that hash_pbkdf2() implements PBKDF2-HMAC-* |
|
- | 636 | if ($algo == 'sha3-512') return 100000; |
|
- | 637 | else if ($algo == 'sha3-384') return 100000; |
|
- | 638 | else if ($algo == 'sha3-256') return 100000; |
|
- | 639 | else if ($algo == 'sha3-224') return 100000; |
|
- | 640 | else if ($algo == 'sha512') return 210000; // value by owasp.org cheatcheat (28 February 2023) |
|
- | 641 | else if ($algo == 'sha512/256') return 210000; // value by owasp.org cheatcheat (28 February 2023) |
|
- | 642 | else if ($algo == 'sha512/224') return 210000; // value by owasp.org cheatcheat (28 February 2023) |
|
- | 643 | else if ($algo == 'sha384') return 600000; |
|
- | 644 | else if ($algo == 'sha256') return 600000; // value by owasp.org cheatcheat (28 February 2023) |
|
- | 645 | else if ($algo == 'sha224') return 600000; |
|
- | 646 | else if ($algo == 'sha1') return 1300000; // value by owasp.org cheatcheat (28 February 2023) |
|
- | 647 | else if ($algo == 'md5') return 5000000; |
|
- | 648 | else return 5000; |
|
- | 649 | } |
|
- | 650 | } |
|
- | 651 | ||
628 | // --- Part 5: Selftest |
652 | // --- Part 6: Selftest |
629 | 653 | ||
630 | /* |
654 | /* |
631 | for ($i=0; $i<9999; $i++) { |
655 | for ($i=0; $i<9999; $i++) { |
632 | assert($i===base64_int_decode(base64_int_encode($i,4))); |
656 | assert($i===base64_int_decode(base64_int_encode($i,4))); |
633 | } |
657 | } |
Line 679... | Line 703... | ||
679 | 'algo' => 'sha3-256', |
703 | 'algo' => 'sha3-256', |
680 | 'mode' => 'pbkdf2', |
704 | 'mode' => 'pbkdf2', |
681 | 'iterations' => 0 |
705 | 'iterations' => 0 |
682 | ))); |
706 | ))); |
683 | 707 | ||
- | 708 | assert(vts_password_verify($password,$dummy = vts_password_hash($password, PASSWORD_VTS_MCF1, array( |
|
- | 709 | 'algo' => 'sha3-512', |
|
- | 710 | 'mode' => 'sps', |
|
- | 711 | 'iterations' => 2 |
|
- | 712 | )))); |
|
684 | echo "OK, password $password\n"; |
713 | //echo "'$dummy' ".strlen($dummy)."\n"; |
- | 714 | //var_dump(vts_password_get_info($dummy)); |
|
- | 715 | assert(false===vts_password_needs_rehash($dummy,PASSWORD_VTS_MCF1,array( |
|
- | 716 | 'salt_length' => 51, |
|
- | 717 | 'algo' => 'sha3-512', |
|
- | 718 | 'mode' => 'sps', |
|
- | 719 | 'iterations' => 2 |
|
- | 720 | ))); |
|
- | 721 | assert(true===vts_password_needs_rehash($dummy,PASSWORD_VTS_MCF1,array( |
|
- | 722 | 'salt_length' => 50, |
|
- | 723 | 'algo' => 'sha3-256', |
|
- | 724 | 'mode' => 'sps', |
|
- | 725 | 'iterations' => 2 |
|
- | 726 | ))); |
|
- | 727 | ||
- | 728 | assert(vts_password_verify($password,$dummy = vts_password_hash($password, PASSWORD_VTS_MCF1, array( |
|
- | 729 | 'algo' => 'sha3-512', |
|
- | 730 | 'mode' => 'hmac', |
|
- | 731 | 'iterations' => 2 |
|
- | 732 | )))); |
|
- | 733 | //echo "'$dummy' ".strlen($dummy)."\n"; |
|
- | 734 | //var_dump(vts_password_get_info($dummy)); |
|
- | 735 | assert(false===vts_password_needs_rehash($dummy,PASSWORD_VTS_MCF1,array( |
|
- | 736 | 'salt_length' => 51, |
|
- | 737 | 'algo' => 'sha3-512', |
|
- | 738 | 'mode' => 'hmac', |
|
- | 739 | 'iterations' => 2 |
|
- | 740 | ))); |
|
- | 741 | assert(true===vts_password_needs_rehash($dummy,PASSWORD_VTS_MCF1,array( |
|
- | 742 | 'salt_length' => 50, |
|
- | 743 | 'algo' => 'sha3-256', |
|
- | 744 | 'mode' => 'hmac', |
|
- | 745 | 'iterations' => 2 |
|
- | 746 | ))); |
|
685 | */ |
747 | */ |