Subversion Repositories oidplus

Rev

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
*/