Subversion Repositories oidplus

Rev

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