Subversion Repositories php_utils

Rev

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

Rev 71 Rev 72
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 142... Line 142...
142
        }
142
        }
143
 
143
 
144
        $dummy = array_shift($ary);
144
        $dummy = array_shift($ary);
145
        $bin_hash = crypt_radix64_decode($dummy);
145
        $bin_hash = crypt_radix64_decode($dummy);
146
 
146
 
-
 
147
        return array('id'     => $id,
-
 
148
                     'salt'   => $bin_salt,
-
 
149
                     'hash'   => $bin_hash,
147
        return array('id' => $id, 'salt' => $bin_salt, 'hash' => $bin_hash, 'params' => $params);
150
                     'params' => $params);
148
}
151
}
149
 
152
 
150
// --- Part 2: ViaThinkSoft Modular Crypt Format 1.0
153
// --- Part 2: ViaThinkSoft Modular Crypt Format 1.0
151
 
154
 
152
function vts_crypt_version($hash) {
155
function vts_crypt_version($hash) {
Line 155... Line 158...
155
        } else {
158
        } else {
156
                return '0';
159
                return '0';
157
        }
160
        }
158
}
161
}
159
 
162
 
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) {
163
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') {
164
        if ($ver == '1') {
184
                if ($mode == PASSWORD_VTS_MCF1_MODE_SP) {
165
                if ($mode == PASSWORD_VTS_MCF1_MODE_SP) {
185
                        $payload = $str_salt.$str_password;
166
                        $payload = $str_salt.$str_password;
186
                        if (!hash_supported_natively($algo) && str_starts_with($algo, 'sha3-') && method_exists('\bb\Sha3\Sha3', 'hash')) {
-
 
187
                                $bits = explode('-',$algo)[1];
-
 
188
                                $bin_hash = \bb\Sha3\Sha3::hash($payload, $bits, true);
-
 
189
                        } else {
-
 
190
                                $bin_hash = hash($algo, $payload, true);
167
                        $bin_hash = hash_ex($algo, $payload, true);
191
                        }
-
 
192
                } else if ($mode == PASSWORD_VTS_MCF1_MODE_PS) {
168
                } else if ($mode == PASSWORD_VTS_MCF1_MODE_PS) {
193
                        $payload = $str_password.$str_salt;
169
                        $payload = $str_password.$str_salt;
194
                        if (!hash_supported_natively($algo) && str_starts_with($algo, 'sha3-') && method_exists('\bb\Sha3\Sha3', 'hash')) {
-
 
195
                                $bits = explode('-',$algo)[1];
-
 
196
                                $bin_hash = \bb\Sha3\Sha3::hash($payload, $bits, true);
-
 
197
                        } else {
-
 
198
                                $bin_hash = hash($algo, $payload, true);
170
                        $bin_hash = hash_ex($algo, $payload, true);
199
                        }
-
 
200
                } else if ($mode == PASSWORD_VTS_MCF1_MODE_SPS) {
171
                } else if ($mode == PASSWORD_VTS_MCF1_MODE_SPS) {
201
                        $payload = $str_salt.$str_password.$str_salt;
172
                        $payload = $str_salt.$str_password.$str_salt;
202
                        if (!hash_supported_natively($algo) && str_starts_with($algo, 'sha3-') && method_exists('\bb\Sha3\Sha3', 'hash')) {
-
 
203
                                $bits = explode('-',$algo)[1];
-
 
204
                                $bin_hash = \bb\Sha3\Sha3::hash($payload, $bits, true);
-
 
205
                        } else {
-
 
206
                                $bin_hash = hash($algo, $payload, true);
173
                        $bin_hash = hash_ex($algo, $payload, true);
207
                        }
-
 
208
                } else if ($mode == PASSWORD_VTS_MCF1_MODE_HMAC) {
174
                } 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')) {
-
 
210
                                $bits = explode('-',$algo)[1];
-
 
211
                                $bin_hash = \bb\Sha3\Sha3::hash_hmac($str_password, $str_salt, $bits, true);
-
 
212
                        } else {
-
 
213
                                $bin_hash = hash_hmac($algo, $str_password, $str_salt, true);
175
                        $bin_hash = hash_hmac_ex($algo, $str_password, $str_salt, true);
214
                        }
-
 
215
                } else if ($mode == PASSWORD_VTS_MCF1_MODE_PBKDF2) {
176
                } 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')) {
-
 
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);
177
                        $bin_hash = hash_pbkdf2_ex($algo, $str_password, $str_salt, $iterations, 0, true);
227
                        }
-
 
228
                } else {
178
                } else {
229
                        throw new Exception("Invalid VTS crypt version 1 mode. Expect sp, ps, sps, hmac, or pbkdf2.");
179
                        throw new Exception("Invalid VTS crypt version 1 mode. Expect sp, ps, sps, hmac, or pbkdf2.");
230
                }
180
                }
231
                $bin_salt = $str_salt;
181
                $bin_salt = $str_salt;
232
                $params = array();
182
                $params = array();
Line 478... Line 428...
478
                        // and it is not a valid option for vts_password_get_info().
428
                        // and it is not a valid option for vts_password_get_info().
479
                        unset($options['salt_length']);
429
                        unset($options['salt_length']);
480
                }
430
                }
481
 
431
 
482
                // iterations=0 means: Default, depending on the algo
432
                // iterations=0 means: Default, depending on the algo
483
                if (($options2['mode'] == PASSWORD_VTS_MCF1_MODE_PBKDF2) && ($options['iterations'] == 0/*default*/)) {
433
                if (($options['iterations'] == 0/*default*/) && ($options2['mode'] == PASSWORD_VTS_MCF1_MODE_PBKDF2)) {
484
                        $algo = $options2['algo'];
434
                        $algo = $options2['algo'];
485
                        $userland = !hash_pbkdf2_supported_natively($algo) && str_starts_with($algo, 'sha3-') && method_exists('\bb\Sha3\Sha3', 'hash_pbkdf2');
435
                        $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);
436
                        $options['iterations'] = _vts_password_default_iterations($algo, $userland);
487
                }
437
                }
488
        }
438
        }
489
 
439
 
490
        // Check if options match
440
        // Check if options match
491
        if (count($options) !== count($options2)) return true;
441
        if (count($options) !== count($options2)) return true;
Line 508... Line 458...
508
                // Hash created by vts_password_hash(), password_hash(), or crypt()
458
                // Hash created by vts_password_hash(), password_hash(), or crypt()
509
                return password_verify($password, $hash);
459
                return password_verify($password, $hash);
510
        }
460
        }
511
}
461
}
512
 
462
 
-
 
463
// --- Part 4: Functions which include a fallback to a pure-PHP sha3 implementation (requires https://github.com/danielmarschall/php-sha3 )
-
 
464
 
-
 
465
function hash_ex($algo, $data, $binary=false, $options=array()) {
-
 
466
        if (!hash_supported_natively($algo) && str_starts_with($algo, 'sha3-') && method_exists('\bb\Sha3\Sha3', 'hash')) {
-
 
467
                $bits = explode('-',$algo)[1];
-
 
468
                $hash = \bb\Sha3\Sha3::hash($data, $bits, $binary);
-
 
469
        } else {
-
 
470
                $hash = hash($algo, $data, $binary);
-
 
471
        }
-
 
472
        return $hash;
-
 
473
}
-
 
474
 
-
 
475
function hash_hmac_ex($algo, $data, $key, $binary=false) {
-
 
476
        if (!hash_hmac_supported_natively($algo) && str_starts_with($algo, 'sha3-') && method_exists('\bb\Sha3\Sha3', 'hash_hmac')) {
-
 
477
                $bits = explode('-',$algo)[1];
-
 
478
                $hash = \bb\Sha3\Sha3::hash_hmac($data, $key, $bits, $binary);
-
 
479
        } else {
-
 
480
                $hash = hash_hmac($algo, $data, $key, $binary);
-
 
481
        }
-
 
482
        return $hash;
-
 
483
}
-
 
484
 
-
 
485
function hash_pbkdf2_ex($algo, $password, $salt, &$iterations=0, $length=0, $binary=false) {
-
 
486
        if (!hash_pbkdf2_supported_natively($algo) && str_starts_with($algo, 'sha3-') && method_exists('\bb\Sha3\Sha3', 'hash_pbkdf2')) {
-
 
487
                if ($iterations == 0/*default*/) {
-
 
488
                        $iterations = _vts_password_default_iterations($algo, true);
-
 
489
                }
-
 
490
                $bits = explode('-',$algo)[1];
-
 
491
                $hash = \bb\Sha3\Sha3::hash_pbkdf2($password, $salt, $iterations, $bits, $length, $binary);
-
 
492
        } else {
-
 
493
                if ($iterations == 0/*default*/) {
-
 
494
                        $iterations = _vts_password_default_iterations($algo, false);
-
 
495
                }
-
 
496
                $hash = hash_pbkdf2($algo, $password, $salt, $iterations, $length, $binary);
-
 
497
        }
-
 
498
        return $hash;
-
 
499
}
-
 
500
 
513
// --- Part 4: Useful functions required by the crypt-functions
501
// --- Part 5: Useful functions required by the crypt-functions
514
 
502
 
515
define('BASE64_RFC4648_ALPHABET', '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/');
503
define('BASE64_RFC4648_ALPHABET', '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/');
516
define('BASE64_CRYPT_ALPHABET',   './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz');
504
define('BASE64_CRYPT_ALPHABET',   './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz');
517
 
505
 
518
function des_compat_salt($salt_len) {
506
function des_compat_salt($salt_len) {
Line 623... Line 611...
623
                }
611
                }
624
        }
612
        }
625
        return $options;
613
        return $options;
626
}
614
}
627
 
615
 
-
 
616
function _vts_password_default_iterations($algo, $userland) {
-
 
617
        if ($userland) {
-
 
618
                return 100; // because the userland implementation is EXTREMELY slow, we must choose a small value, sorry...
-
 
619
        } else {
-
 
620
                // Recommendations taken from https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#pbkdf2
-
 
621
                // Note that hash_pbkdf2() implements PBKDF2-HMAC-*
-
 
622
                if      ($algo == 'sha3-512')    return  100000;
-
 
623
                else if ($algo == 'sha3-384')    return  100000;
-
 
624
                else if ($algo == 'sha3-256')    return  100000;
-
 
625
                else if ($algo == 'sha3-224')    return  100000;
-
 
626
                else if ($algo == 'sha512')      return  210000; // value by owasp.org cheatcheat (28 February 2023)
-
 
627
                else if ($algo == 'sha512/256')  return  210000; // value by owasp.org cheatcheat (28 February 2023)
-
 
628
                else if ($algo == 'sha512/224')  return  210000; // value by owasp.org cheatcheat (28 February 2023)
-
 
629
                else if ($algo == 'sha384')      return  600000;
-
 
630
                else if ($algo == 'sha256')      return  600000; // value by owasp.org cheatcheat (28 February 2023)
-
 
631
                else if ($algo == 'sha224')      return  600000;
-
 
632
                else if ($algo == 'sha1')        return 1300000; // value by owasp.org cheatcheat (28 February 2023)
-
 
633
                else if ($algo == 'md5')         return 5000000;
-
 
634
                else                             return    5000;
-
 
635
        }
-
 
636
}
-
 
637
 
628
// --- Part 5: Selftest
638
// --- Part 6: Selftest
629
 
639
 
630
/*
-
 
631
for ($i=0; $i<9999; $i++) {
640
for ($i=0; $i<9999; $i++) {
632
        assert($i===base64_int_decode(base64_int_encode($i,4)));
641
        assert($i===base64_int_decode(base64_int_encode($i,4)));
633
}
642
}
634
 
643
 
635
$rnd = random_bytes_ex(50, true, true);
644
$rnd = random_bytes_ex(50, true, true);
Line 680... Line 689...
680
        'mode' => 'pbkdf2',
689
        'mode' => 'pbkdf2',
681
        'iterations' => 0
690
        'iterations' => 0
682
)));
691
)));
683
 
692
 
684
echo "OK, password $password\n";
693
echo "OK, password $password\n";
685
*/
-