Subversion Repositories php_utils

Rev

Rev 73 | Rev 75 | Go to most recent revision | Show entire file | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 73 Rev 74
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 161... Line 162...
161
}
162
}
162
 
163
 
163
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) {
164
        if ($ver == '1') {
165
        if ($ver == '1') {
165
                if ($mode == PASSWORD_VTS_MCF1_MODE_SP) {
166
                if ($mode == PASSWORD_VTS_MCF1_MODE_SP) {
166
                        $payload = $str_salt.$str_password;
167
                        $bin_hash = hash_ex($algo, $str_salt.$str_password, true);
-
 
168
                        for ($i=0; $i<$iterations; $i++) {
167
                        $bin_hash = hash_ex($algo, $payload, true);
169
                                $bin_hash = hash_ex($algo, $str_salt.$bin_hash.$i, true);
-
 
170
                        }
168
                } else if ($mode == PASSWORD_VTS_MCF1_MODE_PS) {
171
                } else if ($mode == PASSWORD_VTS_MCF1_MODE_PS) {
169
                        $payload = $str_password.$str_salt;
172
                        $bin_hash = hash_ex($algo, $str_password.$str_salt, true);
-
 
173
                        for ($i=0; $i<$iterations; $i++) {
170
                        $bin_hash = hash_ex($algo, $payload, true);
174
                                $bin_hash = hash_ex($algo, $bin_hash.$i.$str_salt, true);
-
 
175
                        }
171
                } else if ($mode == PASSWORD_VTS_MCF1_MODE_SPS) {
176
                } else if ($mode == PASSWORD_VTS_MCF1_MODE_SPS) {
172
                        $payload = $str_salt.$str_password.$str_salt;
177
                        $bin_hash = hash_ex($algo, $str_salt.$str_password.$str_salt, true);
-
 
178
                        for ($i=0; $i<$iterations; $i++) {
173
                        $bin_hash = hash_ex($algo, $payload, true);
179
                                $bin_hash = hash_ex($algo, $str_salt.$bin_hash.$i.$str_salt, true);
-
 
180
                        }
174
                } else if ($mode == PASSWORD_VTS_MCF1_MODE_HMAC) {
181
                } else if ($mode == PASSWORD_VTS_MCF1_MODE_HMAC) {
175
                        $bin_hash = hash_hmac_ex($algo, $str_password, $str_salt, true);
182
                        $bin_hash = hash_hmac_ex($algo, $str_password, $str_salt, true);
-
 
183
                        for ($i=0; $i<$iterations; $i++) {
-
 
184
                                // https://security.stackexchange.com/questions/149299/rounds-in-a-hashing-function
-
 
185
                                $bin_hash = hash_hmac_ex($algo, $str_password, $bin_hash.$i, true);
-
 
186
                        }
176
                } else if ($mode == PASSWORD_VTS_MCF1_MODE_PBKDF2) {
187
                } else if ($mode == PASSWORD_VTS_MCF1_MODE_PBKDF2) {
177
                        $bin_hash = hash_pbkdf2_ex($algo, $str_password, $str_salt, $iterations, 0, true);
188
                        $bin_hash = hash_pbkdf2_ex($algo, $str_password, $str_salt, $iterations, 0, true);
178
                } else {
189
                } else {
179
                        throw new Exception("Invalid VTS crypt version 1 mode. Expect sp, ps, sps, hmac, or pbkdf2.");
190
                        throw new Exception("Invalid VTS crypt version 1 mode. Expect sp, ps, sps, hmac, or pbkdf2.");
180
                }
191
                }
181
                $bin_salt = $str_salt;
192
                $bin_salt = $str_salt;
182
                $params = array();
193
                $params = array();
183
                $params['a'] = $algo;
194
                $params['a'] = $algo;
184
                $params['m'] = $mode;
195
                $params['m'] = $mode;
185
                if ($mode == PASSWORD_VTS_MCF1_MODE_PBKDF2) $params['i'] = $iterations;
196
                if ($iterations != 0) $params['i'] = $iterations; // i can be omitted if it is 0.
186
                return crypt_modular_format_encode(OID_MCF_VTS_V1, $bin_salt, $bin_hash, $params);
197
                return crypt_modular_format_encode(OID_MCF_VTS_V1, $bin_salt, $bin_hash, $params);
187
        } else {
198
        } else {
188
                throw new Exception("Invalid VTS crypt version, expect 1.");
199
                throw new Exception("Invalid VTS crypt version, expect 1.");
189
        }
200
        }
190
}
201
}
Line 208... Line 219...
208
 
219
 
209
                if ($mode == PASSWORD_VTS_MCF1_MODE_PBKDF2) {
220
                if ($mode == PASSWORD_VTS_MCF1_MODE_PBKDF2) {
210
                        if (!isset($params['i'])) throw new Exception('Param "i" (iterations) missing');
221
                        if (!isset($params['i'])) throw new Exception('Param "i" (iterations) missing');
211
                        $iterations = $params['i'];
222
                        $iterations = $params['i'];
212
                } else {
223
                } else {
213
                        $iterations = 0;
224
                        $iterations = isset($params['i']) ? $params['i'] : 0;
214
                }
225
                }
215
 
226
 
216
                // Create a VTS MCF 1.0 hash based on the parameters of $hash and the password $password
227
                // Create a VTS MCF 1.0 hash based on the parameters of $hash and the password $password
217
                $calc_authkey_1 = vts_crypt_hash($algo, $password, $bin_salt, $ver, $mode, $iterations);
228
                $calc_authkey_1 = vts_crypt_hash($algo, $password, $bin_salt, $ver, $mode, $iterations);
218
 
229
 
Line 264... Line 275...
264
                $options['mode'] = $mcf['params']['m'];
275
                $options['mode'] = $mcf['params']['m'];
265
 
276
 
266
                if ($options['mode'] == PASSWORD_VTS_MCF1_MODE_PBKDF2) {
277
                if ($options['mode'] == PASSWORD_VTS_MCF1_MODE_PBKDF2) {
267
                        if (!isset($mcf['params']['i'])) throw new Exception('Param "i" (iterations) missing');
278
                        if (!isset($mcf['params']['i'])) throw new Exception('Param "i" (iterations) missing');
268
                        $options['iterations'] = (int)$mcf['params']['i'];
279
                        $options['iterations'] = (int)$mcf['params']['i'];
-
 
280
                } else {
-
 
281
                        $options['iterations'] = isset($mcf['params']['i']) ? (int)$mcf['params']['i'] : 0;
269
                }
282
                }
270
 
283
 
271
                return array(
284
                return array(
272
                        "algo" => PASSWORD_VTS_MCF1,
285
                        "algo" => PASSWORD_VTS_MCF1,
273
                        "algoName" => "vts-mcf-v1",
286
                        "algoName" => "vts-mcf-v1",
Line 427... Line 440...
427
                        // but it is not a valid option inside the MCF options
440
                        // but it is not a valid option inside the MCF options
428
                        // and it is not a valid option for vts_password_get_info().
441
                        // and it is not a valid option for vts_password_get_info().
429
                        unset($options['salt_length']);
442
                        unset($options['salt_length']);
430
                }
443
                }
431
 
444
 
432
                // iterations=0 means: Default, depending on the algo
445
                // For PBKDF2, iterations=0 means: Default, depending on the algo
433
                if (($options['iterations'] == 0/*default*/) && ($options2['mode'] == PASSWORD_VTS_MCF1_MODE_PBKDF2)) {
446
                if (($options['iterations'] == 0/*default*/) && ($options2['mode'] == PASSWORD_VTS_MCF1_MODE_PBKDF2)) {
434
                        $algo = $options2['algo'];
447
                        $algo = $options2['algo'];
435
                        $userland = !hash_pbkdf2_supported_natively($algo) && str_starts_with($algo, 'sha3-') && method_exists('\bb\Sha3\Sha3', 'hash_pbkdf2');
448
                        $userland = !hash_pbkdf2_supported_natively($algo) && str_starts_with($algo, 'sha3-') && method_exists('\bb\Sha3\Sha3', 'hash_pbkdf2');
436
                        $options['iterations'] = _vts_password_default_iterations($algo, $userland);
449
                        $options['iterations'] = _vts_password_default_iterations($algo, $userland);
437
                }
450
                }
Line 605... Line 618...
605
                if ($options['mode'] == PASSWORD_VTS_MCF1_MODE_PBKDF2) {
618
                if ($options['mode'] == PASSWORD_VTS_MCF1_MODE_PBKDF2) {
606
                        if (!isset($options['iterations'])) {
619
                        if (!isset($options['iterations'])) {
607
                                $options['iterations'] = PASSWORD_VTS_MCF1_DEFAULT_ITERATIONS;
620
                                $options['iterations'] = PASSWORD_VTS_MCF1_DEFAULT_ITERATIONS;
608
                        }
621
                        }
609
                } else {
622
                } else {
610
                        unset($options['iterations']);
623
                        $options['iterations'] = isset($options['iterations']) ? $options['iterations'] : 0;
611
                }
624
                }
612
        }
625
        }
613
        return $options;
626
        return $options;
614
}
627
}
615
 
628
 
Line 689... Line 702...
689
        'algo' => 'sha3-256',
702
        'algo' => 'sha3-256',
690
        'mode' => 'pbkdf2',
703
        'mode' => 'pbkdf2',
691
        'iterations' => 0
704
        'iterations' => 0
692
)));
705
)));
693
 
706
 
-
 
707
assert(vts_password_verify($password,$dummy = vts_password_hash($password, PASSWORD_VTS_MCF1, array(
-
 
708
        'algo' => 'sha3-512',
-
 
709
        'mode' => 'sps',
-
 
710
        'iterations' => 2
-
 
711
))));
-
 
712
//echo "'$dummy' ".strlen($dummy)."\n";
-
 
713
//var_dump(vts_password_get_info($dummy));
-
 
714
assert(false===vts_password_needs_rehash($dummy,PASSWORD_VTS_MCF1,array(
-
 
715
        'salt_length' => 51,
-
 
716
        'algo' => 'sha3-512',
-
 
717
        'mode' => 'sps',
-
 
718
        'iterations' => 2
-
 
719
)));
-
 
720
assert(true===vts_password_needs_rehash($dummy,PASSWORD_VTS_MCF1,array(
-
 
721
        'salt_length' => 50,
-
 
722
        'algo' => 'sha3-256',
-
 
723
        'mode' => 'sps',
-
 
724
        'iterations' => 2
-
 
725
)));
-
 
726
 
-
 
727
assert(vts_password_verify($password,$dummy = vts_password_hash($password, PASSWORD_VTS_MCF1, array(
-
 
728
        'algo' => 'sha3-512',
-
 
729
        'mode' => 'hmac',
-
 
730
        'iterations' => 2
-
 
731
))));
-
 
732
//echo "'$dummy' ".strlen($dummy)."\n";
-
 
733
//var_dump(vts_password_get_info($dummy));
-
 
734
assert(false===vts_password_needs_rehash($dummy,PASSWORD_VTS_MCF1,array(
-
 
735
        'salt_length' => 51,
-
 
736
        'algo' => 'sha3-512',
-
 
737
        'mode' => 'hmac',
-
 
738
        'iterations' => 2
-
 
739
)));
-
 
740
assert(true===vts_password_needs_rehash($dummy,PASSWORD_VTS_MCF1,array(
-
 
741
        'salt_length' => 50,
-
 
742
        'algo' => 'sha3-256',
-
 
743
        'mode' => 'hmac',
-
 
744
        'iterations' => 2
-
 
745
)));
694
*/
746
*/