Subversion Repositories oidplus

Rev

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

Rev Author Line No. Line
635 daniel-mar 1
<?php
2
 
3
/**
4
 * WEID<=>OID Converter
5
 * (c) Webfan.de, ViaThinkSoft
771 daniel-mar 6
 * Revision 2022-03-06
635 daniel-mar 7
 **/
8
 
683 daniel-mar 9
// What is a WEID?
10
//     A WEID (WEhowski IDentifier) is an alternative representation of an
11
//     OID (Object IDentifier) defined by Till Wehowski.
12
//     In OIDs, arcs are in decimal base 10. In WEIDs, the arcs are in base 36.
13
//     Also, each WEID has a check digit at the end (called WeLohn Check Digit).
14
//
15
// Changes in the December 2021 definition by Daniel Marschall:
16
//     - There are several classes of WEIDs which have different OID bases:
17
//           "Class C" WEID:  weid:EXAMPLE-3      (base .1.3.6.1.4.1.37553.8.)
18
//                            oid:1.3.6.1.4.1.37553.8.32488192274
19
//           "Class B" WEID:  weid:pen:SX0-7PR-6  (base .1.3.6.1.4.1.)
20
//                            oid:1.3.6.1.4.1.37476.9999
21
//           "Class A" WEID:  weid:root:2-RR-2    (base .)
22
//                            oid:2.999
23
//     - The namespace (weid:, weid:pen:, weid:root:) is now case insensitive.
24
//     - Padding with '0' characters is valid (e.g. weid:000EXAMPLE-3)
25
//       The paddings do not count into the WeLuhn check-digit.
26
 
635 daniel-mar 27
class WeidOidConverter {
28
 
29
        protected static function weLuhnGetCheckDigit($str) {
683 daniel-mar 30
                // Padding zeros don't count to the check digit (December 2021)
31
                $ary = explode('-', $str);
686 daniel-mar 32
                foreach ($ary as &$a) {
33
                        $a = ltrim($a, '0');
34
                        if ($a === '') $a = '0';
35
                }
683 daniel-mar 36
                $str = implode('-', $ary);
37
 
686 daniel-mar 38
                // remove separators of the WEID string
39
                $wrkstr = str_replace('-', '', $str);
40
 
41
                // Replace 'a' with '10', 'b' with '11', etc.
42
                for ($i=0; $i<26; $i++) {
635 daniel-mar 43
                        $wrkstr = str_ireplace(chr(ord('a')+$i), (string)($i+10), $wrkstr);
44
                }
686 daniel-mar 45
 
46
                // At the end, $wrkstr should only contain digits! Verify it!
47
                for ($i=0; $i<strlen($wrkstr); $i++) {
48
                        if (($wrkstr[$i]<'0') || ($wrkstr[$i]>'9')) return false;
49
                }
50
 
51
                // Now do the standard Luhn algorithm
635 daniel-mar 52
                $nbdigits = strlen($wrkstr);
686 daniel-mar 53
                $parity = $nbdigits & 1; // mod 2
635 daniel-mar 54
                $sum = 0;
55
                for ($n=$nbdigits-1; $n>=0; $n--) {
686 daniel-mar 56
                        $digit = (int)$wrkstr[$n];
635 daniel-mar 57
                        if (($n & 1) != $parity) $digit *= 2;
58
                        if ($digit > 9) $digit -= 9;
59
                        $sum += $digit;
60
                }
61
                return ($sum%10) == 0 ? 0 : 10-($sum%10);
62
        }
63
 
771 daniel-mar 64
        private static function oidSanitize($oid) {
65
                $oid = trim($oid);
66
 
67
                if (substr($oid,0,1) == '.') $oid = substr($oid,1); // remove leading dot
68
 
69
                if ($oid != '') {
70
                        $elements = explode('.', $oid);
71
                        foreach ($elements as &$elem) {
72
                                if (trim($elem) == '') return false;
73
 
74
                                if (!preg_match('/^\d+$/', $elem, $m)) return false;
75
 
76
                                if (preg_match('/^0+$/', $elem, $m)) {
77
                                        $elem = '0';
78
                                } else {
79
                                        $elem = preg_replace('/^0+/', '', $elem);
80
                                }
81
                        };
82
                        $oid = implode('.', $elements);
83
 
84
                        if ((count($elements) > 0) && ($elements[0] != '0') && ($elements[0] != '1') && ($elements[0] != '2')) return false;
85
                        if ((count($elements) > 1) && (($elements[0] == '0') || ($elements[0] == '1')) && ((strlen($elements[1]) > 2) || ($elements[1] > 39))) return false;
86
                }
87
 
88
                return $oid;
89
        }
90
 
683 daniel-mar 91
        // Translates a weid to an oid
92
        // "weid:EXAMPLE-3" becomes "1.3.6.1.4.1.37553.8.32488192274"
93
        // If it failed (e.g. wrong namespace, wrong checksum, etc.) then false is returned.
94
        // If the weid ends with '?', then it will be replaced with the checksum,
95
        // e.g. weid:EXAMPLE-? becomes weid:EXAMPLE-3
96
        public static function weid2oid(&$weid) {
771 daniel-mar 97
                $weid = trim($weid);
635 daniel-mar 98
 
683 daniel-mar 99
                $p = strrpos($weid,':');
100
                $namespace = substr($weid, 0, $p+1);
101
                $rest = substr($weid, $p+1);
102
 
103
                $namespace = strtolower($namespace); // namespace is case insensitive
104
                if ($namespace == 'weid:') {
105
                        // Class C
106
                        $base = '1-3-6-1-4-1-SZ5-8';
107
                } else if ($namespace == 'weid:pen:') {
108
                        // Class B
109
                        $base = '1-3-6-1-4-1';
110
                } else if ($namespace == 'weid:root:') {
111
                        // Class A
112
                        $base = '';
113
                } else {
114
                        // Wrong namespace
115
                        return false;
116
                }
117
 
118
                $weid = $rest;
119
 
120
                $elements = array_merge(($base != '') ? explode('-', $base) : array(), explode('-', $weid));
771 daniel-mar 121
 
122
                foreach ($elements as $elem) {
123
                        if ($elem == '') return false;
124
                }
125
 
635 daniel-mar 126
                $actual_checksum = array_pop($elements);
127
                $expected_checksum = self::weLuhnGetCheckDigit(implode('-',$elements));
128
                if ($actual_checksum != '?') {
771 daniel-mar 129
                        if ($actual_checksum != $expected_checksum) {
130
                                return false; // wrong checksum
131
                        }
635 daniel-mar 132
                } else {
133
                        // If checksum is '?', it will be replaced by the actual checksum,
134
                        // e.g. weid:EXAMPLE-? becomes weid:EXAMPLE-3
135
                        $weid = str_replace('?', $expected_checksum, $weid);
136
                }
137
                foreach ($elements as &$arc) {
138
                        //$arc = strtoupper(base_convert($arc, 36, 10));
139
                        $arc = strtoupper(self::base_convert_bigint($arc, 36, 10));
140
                }
771 daniel-mar 141
                $oid = implode('.', $elements);
635 daniel-mar 142
 
750 daniel-mar 143
                $weid = strtolower($namespace) . strtoupper($weid); // add namespace again
683 daniel-mar 144
 
771 daniel-mar 145
                $oid = self::oidSanitize($oid);
146
                if ($oid === false) return false;
147
 
148
                return $oid;
635 daniel-mar 149
        }
150
 
683 daniel-mar 151
        // Converts an OID to WEID
152
        // "1.3.6.1.4.1.37553.8.32488192274" becomes "weid:EXAMPLE-3"
153
        public static function oid2weid($oid) {
771 daniel-mar 154
                $oid = self::oidSanitize($oid);
155
                if ($oid === false) return false;
683 daniel-mar 156
 
157
                if ($oid !== '') {
158
                        $elements = explode('.', $oid);
159
                        foreach ($elements as &$arc) {
160
                                //$arc = strtoupper(base_convert($arc, 10, 36));
161
                                $arc = strtoupper(self::base_convert_bigint($arc, 10, 36));
162
                        }
163
                        $weidstr = implode('-', $elements);
164
                } else {
165
                        $weidstr = '';
635 daniel-mar 166
                }
167
 
683 daniel-mar 168
                $is_class_c = (strpos($weidstr, '1-3-6-1-4-1-SZ5-8-') === 0) ||
169
                              ($weidstr === '1-3-6-1-4-1-SZ5-8');
170
                $is_class_b = ((strpos($weidstr, '1-3-6-1-4-1-') === 0) ||
171
                              ($weidstr === '1-3-6-1-4-1'))
172
                              && !$is_class_c;
173
                $is_class_a = !$is_class_b && !$is_class_c;
635 daniel-mar 174
 
683 daniel-mar 175
                $checksum = self::weLuhnGetCheckDigit($weidstr);
176
 
177
                if ($is_class_c) {
178
                        $weidstr = substr($weidstr, strlen('1-3-6-1-4-1-SZ5-8-'));
179
                        $namespace = 'weid:';
180
                } else if ($is_class_b) {
181
                        $weidstr = substr($weidstr, strlen('1-3-6-1-4-1-'));
182
                        $namespace = 'weid:pen:';
183
                } else if ($is_class_a) {
184
                        // $weidstr stays
185
                        $namespace = 'weid:root:';
686 daniel-mar 186
                } else {
187
                        // should not happen
188
                        return false;
683 daniel-mar 189
                }
190
 
771 daniel-mar 191
                $weid = $namespace . ($weidstr == '' ? $checksum : $weidstr . '-' . $checksum);
192
 
193
                return $weid;
635 daniel-mar 194
        }
195
 
196
        protected static function base_convert_bigint($numstring, $frombase, $tobase) {
197
                $frombase_str = '';
198
                for ($i=0; $i<$frombase; $i++) {
199
                        $frombase_str .= strtoupper(base_convert((string)$i, 10, 36));
200
                }
201
 
202
                $tobase_str = '';
203
                for ($i=0; $i<$tobase; $i++) {
204
                        $tobase_str .= strtoupper(base_convert((string)$i, 10, 36));
205
                }
206
 
207
                $length = strlen($numstring);
208
                $result = '';
209
                $number = array();
210
                for ($i = 0; $i < $length; $i++) {
211
                        $number[$i] = stripos($frombase_str, $numstring[$i]);
212
                }
213
                do { // Loop until whole number is converted
214
                        $divide = 0;
215
                        $newlen = 0;
216
                        for ($i = 0; $i < $length; $i++) { // Perform division manually (which is why this works with big numbers)
217
                                $divide = $divide * $frombase + $number[$i];
218
                                if ($divide >= $tobase) {
219
                                        $number[$newlen++] = (int)($divide / $tobase);
220
                                        $divide = $divide % $tobase;
221
                                } else if ($newlen > 0) {
222
                                        $number[$newlen++] = 0;
223
                                }
224
                        }
225
                        $length = $newlen;
226
                        $result = $tobase_str[$divide] . $result; // Divide is basically $numstring % $tobase (i.e. the new character)
227
                }
228
                while ($newlen != 0);
229
 
230
                return $result;
231
        }
232
}
233
 
234
 
683 daniel-mar 235
# --- Usage Example ---
236
 
635 daniel-mar 237
/*
683 daniel-mar 238
echo "Class C tests:\n\n";
635 daniel-mar 239
 
683 daniel-mar 240
var_dump($oid = '1.3.6.1.4.1.37553.8')."\n";
241
var_dump(WeidOidConverter::oid2weid($oid))."\n";
242
$weid = 'weid:?';
243
var_dump(WeidOidConverter::weid2oid($weid))."\n";
244
var_dump($weid)."\n";
245
echo "\n";
635 daniel-mar 246
 
683 daniel-mar 247
var_dump($oid = '1.3.6.1.4.1.37553.8.32488192274')."\n";
248
var_dump(WeidOidConverter::oid2weid($oid))."\n";
249
$weid = 'weid:EXAMPLE-?';
250
var_dump(WeidOidConverter::weid2oid($weid))."\n";
251
var_dump($weid)."\n";
252
$weid = 'weid:00000example-?';
253
var_dump(WeidOidConverter::weid2oid($weid))."\n";
254
var_dump($weid)."\n";
255
echo "\n";
635 daniel-mar 256
 
683 daniel-mar 257
echo "Class B tests:\n\n";
635 daniel-mar 258
 
683 daniel-mar 259
var_dump($oid = '1.3.6.1.4.1')."\n";
260
var_dump(WeidOidConverter::oid2weid($oid))."\n";
261
$weid = 'weid:pen:?';
262
var_dump(WeidOidConverter::weid2oid($weid))."\n";
263
var_dump($weid)."\n";
264
echo "\n";
635 daniel-mar 265
 
683 daniel-mar 266
var_dump($oid = '1.3.6.1.4.1.37553.7.99.99.99')."\n";
267
var_dump(WeidOidConverter::oid2weid($oid))."\n";
268
$weid = 'weid:pen:SZ5-7-2R-2R-2R-?';
269
var_dump(WeidOidConverter::weid2oid($weid))."\n";
270
var_dump($weid)."\n";
271
$weid = 'weid:pen:000SZ5-7-02R-00002R-002r-?';
272
var_dump(WeidOidConverter::weid2oid($weid))."\n";
273
var_dump($weid)."\n";
274
echo "\n";
275
 
276
var_dump($oid = '1.3.6.1.4.1.37476.9999')."\n";
277
var_dump(WeidOidConverter::oid2weid($oid))."\n";
278
$weid = 'weid:pen:SX0-7PR-?';
279
var_dump(WeidOidConverter::weid2oid($weid))."\n";
280
var_dump($weid)."\n";
281
echo "\n";
282
 
283
echo "Class A tests:\n\n";
284
 
285
var_dump($oid = '')."\n";
286
var_dump(WeidOidConverter::oid2weid($oid))."\n";
287
$weid = 'weid:root:?';
288
var_dump(WeidOidConverter::weid2oid($weid))."\n";
289
var_dump($weid)."\n";
290
echo "\n";
291
 
292
var_dump($oid = '.2.999')."\n";
293
var_dump(WeidOidConverter::oid2weid($oid))."\n";
294
$weid = 'weid:root:2-RR-?';
295
var_dump(WeidOidConverter::weid2oid($weid))."\n";
296
var_dump($weid)."\n";
297
echo "\n";
298
 
299
var_dump($oid = '2.999')."\n";
300
var_dump(WeidOidConverter::oid2weid($oid))."\n";
301
$weid = 'weid:root:2-RR-?';
302
var_dump(WeidOidConverter::weid2oid($weid))."\n";
303
var_dump($weid)."\n";
304
echo "\n";
635 daniel-mar 305
*/