Subversion Repositories oidplus

Rev

Rev 1090 | Rev 1096 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
1090 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
 
1091 daniel-mar 49
Like most Crypt-hashes, <salt> and <hash> are Radix64 coded
50
with alphabet './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' and no padding.
51
 
1090 daniel-mar 52
Link to the online specification:
53
        https://oidplus.viathinksoft.com/oidplus/?goto=oid%3A1.3.6.1.4.1.37476.3.0.1.1
54
 
55
Reference implementation in PHP:
56
        https://github.com/danielmarschall/php_utils/blob/master/vts_crypt.inc.php
57
 
58
*/
59
 
60
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) }
61
 
1091 daniel-mar 62
define('BASE64_RFC4648_ALPHABET', '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/');
63
define('BASE64_CRYPT_ALPHABET',   './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz');
1090 daniel-mar 64
 
65
function crypt_radix64_encode($str) {
66
        $x = $str;
67
        $x = base64_encode($x);
1091 daniel-mar 68
        $x = rtrim($x, '=');
69
        $x = strtr($x, BASE64_RFC4648_ALPHABET, BASE64_CRYPT_ALPHABET);
1090 daniel-mar 70
        return $x;
71
}
72
 
73
function crypt_radix64_decode($str) {
74
        $x = $str;
1091 daniel-mar 75
        $x = strtr($x, BASE64_CRYPT_ALPHABET, BASE64_RFC4648_ALPHABET);
1090 daniel-mar 76
        $x = base64_decode($x);
77
        return $x;
78
}
79
 
1091 daniel-mar 80
// assert(crypt_radix64_decode(crypt_radix64_encode('TEST123')) === 'TEST123');
1090 daniel-mar 81
 
82
function crypt_modular_format($id, $bin_salt, $bin_hash, $params=null) {
83
        // $<id>[$<param>=<value>(,<param>=<value>)*][$<salt>[$<hash>]]
84
        $out = '$'.$id;
85
        if (!is_null($params)) {
86
                $ary_params = array();
87
                //ksort($params);
88
                foreach ($params as $name => $value) {
89
                        $ary_params[] = "$name=$value";
90
                }
91
                $out .= '$'.implode(',',$ary_params);
92
        }
93
        $out .= '$'.crypt_radix64_encode($bin_salt);
94
        $out .= '$'.crypt_radix64_encode($bin_hash);
95
        return $out;
96
}
97
 
98
function crypt_modular_format_decode($mcf) {
99
        $ary = explode('$', $mcf);
100
 
101
        $dummy = array_shift($ary);
102
        if ($dummy !== '') return false;
103
 
104
        $dummy = array_shift($ary);
105
        $id = $dummy;
106
 
107
        $params = array();
108
        $dummy = array_shift($ary);
109
        if (strpos($dummy, '=') !== false) {
110
                $params_ary = explode(',',$dummy);
111
                foreach ($params_ary as $param) {
112
                        $bry = explode('=', $param, 2);
113
                        if (count($bry) > 1) {
114
                                $params[$bry[0]] = $bry[1];
115
                        }
116
                }
117
        } else {
118
                array_unshift($ary, $dummy);
119
        }
120
 
121
        if (count($ary) > 1) {
122
                $dummy = array_shift($ary);
123
                $bin_salt = crypt_radix64_decode($dummy);
124
        } else {
125
                $bin_salt = '';
126
        }
127
 
128
        $dummy = array_shift($ary);
129
        $bin_hash = crypt_radix64_decode($dummy);
130
 
131
        return array('id' => $id, 'salt' => $bin_salt, 'hash' => $bin_hash, 'params' => $params);
132
}
133
 
134
function vts_crypt($algo, $str_password, $str_salt, $ver='1', $mode='ps') {
135
        if ($ver == '1') {
136
                if ($mode == 'sp') {
137
                        $payload = $str_salt.$str_password;
138
                        if (($algo === 'sha3-512') && !in_array($algo, hash_algos()) && function_exists('sha3_512')) {
139
                                $bin_hash = sha3_512($payload, true);
140
                        } else {
141
                                $bin_hash = hash($algo, $payload, true);
142
                        }
143
                } else if ($mode == 'ps') {
144
                        $payload = $str_password.$str_salt;
145
                        if (($algo === 'sha3-512') && !in_array($algo, hash_algos()) && function_exists('sha3_512')) {
146
                                $bin_hash = sha3_512($payload, true);
147
                        } else {
148
                                $bin_hash = hash($algo, $payload, true);
149
                        }
150
                } else if ($mode == 'sps') {
151
                        $payload = $str_salt.$str_password.$str_salt;
152
                        if (($algo === 'sha3-512') && !in_array($algo, hash_algos()) && function_exists('sha3_512')) {
153
                                $bin_hash = sha3_512($payload, true);
154
                        } else {
155
                                $bin_hash = hash($algo, $payload, true);
156
                        }
157
                } else if ($mode == 'hmac') {
158
                        // 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
159
                        if (($algo === 'sha3-512') && !in_array($algo, hash_algos()) && function_exists('sha3_512_hmac')) {
160
                                $bin_hash = sha3_512_hmac($str_password, $str_salt, true);
161
                        } else {
162
                                $bin_hash = hash_hmac($algo, $str_password, $str_salt, true);
163
                        }
164
                } else {
165
                        throw new Exception("Invalid VTS crypt version 1 mode. Expect sp, ps, sps, or hmac.");
166
                }
167
                $bin_salt = $str_salt;
168
                return crypt_modular_format(OID_MCF_VTS_V1, $bin_salt, $bin_hash, array('a'=>$algo,'m'=>$mode));
169
        } else {
170
                throw new Exception("Invalid VTS crypt version, expect 1.");
171
        }
172
}