Subversion Repositories php_utils

Rev

Rev 59 | Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
58 daniel-mar 1
<?php
2
 
3
/*
4
 * ViaThinkSoft Modular Crypt Format 1.0
5
 * Revision 2023-02-26
6
 * Copyright 2023 Daniel Marschall, ViaThinkSoft
7
 *
8
 * Licensed under the Apache License, Version 2.0 (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
11
 *
12
 *     http://www.apache.org/licenses/LICENSE-2.0
13
 *
14
 * Unless required by applicable law or agreed to in writing, software
15
 * distributed under the License is distributed on an "AS IS" BASIS,
16
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
 * See the License for the specific language governing permissions and
18
 * limitations under the License.
19
 */
20
 
21
/*
22
 
23
ViaThinkSoft Modular Crypt Format 1.0 performs a simple hash or HMAC operation.
24
No key derivation function or iterations are performed.
25
 
26
Format:
27
        $1.3.6.1.4.1.37476.3.0.1.1$a=<algo>,m=<mode>$<salt>$<hash>
28
 
29
where <algo> is any valid hash algorithm (name scheme of PHP hash_algos() preferred), e.g.
30
        sha3-512
31
        sha3-384
32
        sha3-256
33
        sha3-224
34
        sha512
35
        sha512/256
36
        sha512/224
37
        sha384
38
        sha256
39
        sha224
40
        sha1
41
        md5
42
 
43
Valid <mode> :
44
        sp = salt + password
45
        ps = password + salt
46
        sps = salt + password + salt
47
        hmac = HMAC (salt is the key)
48
 
49
Link to the online specification:
50
        https://oidplus.viathinksoft.com/oidplus/?goto=oid%3A1.3.6.1.4.1.37476.3.0.1.1
51
 
52
Reference implementation in PHP:
53
        https://github.com/danielmarschall/php_utils/blob/master/vts_crypt.inc.php
54
 
55
*/
56
 
57
define('OID_MCF_VTS_V1', '1.3.6.1.4.1.37476.3.0.1.1'); // { iso(1) identified-organization(3) dod(6) internet(1) private(4) enterprise(1) 37476 specifications(3) misc(0) modular-crypt-format(1) vts-crypt-v1(1) }
58
 
59
function crypt_radix64($str) {
60
        $rfc4648_base64 = array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '+', '/', '=');
61
        $crypt_base64   = array('.', '/', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '');
62
 
63
        $x = base64_encode($str);
64
        $x = str_replace($rfc4648_base64, $crypt_base64, $x);
65
        return $x;
66
}
67
 
68
function crypt_modular_format($id, $bin_salt, $bin_hash, $params=null) {
69
        // $<id>[$<param>=<value>(,<param>=<value>)*][$<salt>[$<hash>]]
70
        $out = '$'.$id;
71
        if (!is_null($params)) {
72
                $ary_params = array();
73
                //ksort($params);
74
                foreach ($params as $name => $value) {
75
                        $ary_params[] = "$name=$value";
76
                }
77
                $out .= '$'.implode(',',$ary_params);
78
        }
79
        $out .= '$'.crypt_radix64($bin_salt);
80
        $out .= '$'.crypt_radix64($bin_hash);
81
        return $out;
82
}
83
 
84
function vts_crypt($algo, $str_password, $str_salt, $ver='1', $mode='ps') {
85
        if ($ver == '1') {
86
                if ($mode == 'sp') {
87
                        $payload = $str_salt.$str_password;
88
                        if (($algo === 'sha3-512') && !in_array($algo, hash_algos()) && function_exists('sha3_512')) {
89
                                $bin_hash = sha3_512($payload, true);
90
                        } else {
91
                                $bin_hash = hash($algo, $payload, true);
92
                        }
93
                } else if ($mode == 'ps') {
94
                        $payload = $str_password.$str_salt;
95
                        if (($algo === 'sha3-512') && !in_array($algo, hash_algos()) && function_exists('sha3_512')) {
96
                                $bin_hash = sha3_512($payload, true);
97
                        } else {
98
                                $bin_hash = hash($algo, $payload, true);
99
                        }
100
                } else if ($mode == 'sps') {
101
                        $payload = $str_salt.$str_password.$str_salt;
102
                        if (($algo === 'sha3-512') && !in_array($algo, hash_algos()) && function_exists('sha3_512')) {
103
                                $bin_hash = sha3_512($payload, true);
104
                        } else {
105
                                $bin_hash = hash($algo, $payload, true);
106
                        }
107
                } else if ($mode == 'hmac') {
108
                        // Note: Actually, we should use hash_hmac_algos(), but this requires PHP 7.2, and we would like to stay compatible with PHP 7.0 for now
109
                        if (($algo === 'sha3-512') && !in_array($algo, hash_algos()) && function_exists('sha3_512_hmac')) {
110
                                $bin_hash = sha3_512_hmac($str_password, $str_salt, true);
111
                        } else {
112
                                $bin_hash = hash_hmac($algo, $str_password, $str_salt, true);
113
                        }
114
                } else {
115
                        throw new Exception("Invalid VTS crypt version 1 mode. Expect sp, ps, sps, or hmac.");
116
                }
117
                $bin_salt = $str_salt;
118
                return crypt_modular_format(OID_MCF_VTS_V1, $bin_salt, $bin_hash, array('a'=>$algo,'m'=>$mode));
119
        } else {
120
                throw new Exception("Invalid VTS crypt version, except 1.");
121
        }
122
}
123
 
124
function vts_crypt_convert_from_old_oidplus($authkey, $salt) {
125
        if (preg_match('@^A1([abcd])#(.+):(.+)$@', $authkey, $m)) {
126
                // A1a#hashalgo:X with X being H(salt+password) in hex- or base64-notation
127
                // A1b#hashalgo:X with X being H(password+salt) in hex- or base64-notation
128
                // A1c#hashalgo:X with X being H(salt+password+salt) in hex- or base64-notation
129
                // A1d#hashalgo:X with X being H_HMAC(password,salt) in hex- or base64-notation
130
                $mode = ''; // avoid PHPstan warning
131
                if ($m[1] == 'a') $mode = 'sp';
132
                else if ($m[1] == 'b') $mode = 'ps';
133
                else if ($m[1] == 'c') $mode = 'sps';
134
                else if ($m[1] == 'd') $mode = 'hmac';
135
                else assert(false);
136
                $algo = $m[2];
137
                $bin_salt = $salt;
138
                if (($algo == 'sha3-512') || ($algo == 'sha3-384') || ($algo == 'sha512') || ($algo == 'sha384')) {
139
                        $bin_hash = base64_decode($m[3]);
140
                } else {
141
                        $bin_hash = hex2bin($m[3]);
142
                }
143
                return crypt_modular_format(OID_MCF_VTS_V1, $bin_salt, $bin_hash, array('a'=>$algo,'m'=>$mode));
144
        } else if (preg_match('@^A2#(.+)$@', $authkey, $m)) {
145
                // A2#X with X being sha3(salt+password) in base64-notation
146
                $mode = 'sp';
147
                $algo = 'sha3-512';
148
                $bin_salt = $salt;
149
                $bin_hash = base64_decode($m[1]);
150
                return crypt_modular_format(OID_MCF_VTS_V1, $bin_salt, $bin_hash, array('a'=>$algo,'m'=>$mode));
151
        } else if (preg_match('@^A3#(.+)$@', $authkey, $m)) {
152
                // A3#X with X being bcrypt  [not VTS hash!]
153
                return $m[1];
154
        } else {
155
                // Nothing to convert
156
                return $authkey;
157
        }
158
}