Subversion Repositories oidplus

Rev

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

Rev Author Line No. Line
750 daniel-mar 1
 
2
/**
3
 * WEID<=>OID Converter
4
 * (c) Webfan.de, ViaThinkSoft
5
 * Revision 2022-02-22
6
 **/
7
 
8
// What is a WEID?
9
//     A WEID (WEhowski IDentifier) is an alternative representation of an
10
//     OID (Object IDentifier) defined by Till Wehowski.
11
//     In OIDs, arcs are in decimal base 10. In WEIDs, the arcs are in base 36.
12
//     Also, each WEID has a check digit at the end (called WeLohn Check Digit).
13
//
14
// Changes in the December 2021 definition by Daniel Marschall:
15
//     - There are several classes of WEIDs which have different OID bases:
16
//           "Class C" WEID:  weid:EXAMPLE-3      (base .1.3.6.1.4.1.37553.8.)
17
//                            oid:1.3.6.1.4.1.37553.8.32488192274
18
//           "Class B" WEID:  weid:pen:SX0-7PR-6  (base .1.3.6.1.4.1.)
19
//                            oid:1.3.6.1.4.1.37476.9999
20
//           "Class A" WEID:  weid:root:2-RR-2    (base .)
21
//                            oid:2.999
22
//     - The namespace (weid:, weid:pen:, weid:root:) is now case insensitive.
23
//     - Padding with '0' characters is valid (e.g. weid:000EXAMPLE-3)
24
//       The paddings do not count into the WeLuhn check-digit.
25
 
26
var WeidOidConverter = {
27
 
28
        weLuhnCheckDigit: function(str) {
29
                // Padding zeros don't count to the check digit (December 2021)
30
                var ary = str.split('-');
31
                ary.forEach((o,i,a) => {
32
                        a[i] = a[i].replace(/^0+/, '');
33
                } );
34
                str = ary.join('-');
35
 
36
                // remove separators from the WEID string
37
                var wrkstr = str.replaceAll('-', '');
38
 
39
                // Replace 'a' with '10', 'b' with '1', etc.
40
                for (var i=0; i<26; i++) {
41
                        wrkstr = wrkstr.toLowerCase().replaceAll(String.fromCharCode('a'.charCodeAt(0)+i).toLowerCase(), (10+i));
42
                }
43
 
44
                // At the end, wrkstr should only contain digits! Verify it!
751 daniel-mar 45
                if (!wrkstr.match(/^\d+$/)) {
46
                        console.error("weLuhnCheckDigit: Invalid input");
47
                        return false;
48
                }
750 daniel-mar 49
 
50
                // Now do the standard Luhn algorithm
51
                var nbdigits = wrkstr.length;
52
                var parity = nbdigits & 1; // mod 2
53
                var sum = 0;
54
                for (var n=nbdigits-1; n>=0; n--) {
55
                        var digit = parseInt(wrkstr.substr(n,1));
56
                        if ((n & 1) != parity) digit *= 2;
57
                        if (digit > 9) digit -= 9;
58
                        sum += digit;
59
                }
60
                return (sum%10) == 0 ? 0 : 10-(sum%10);
61
        },
62
 
63
        // Translates a WEID to an OID
64
        // "weid:EXAMPLE-3" becomes "1.3.6.1.4.1.37553.8.32488192274"
65
        // If it failed (e.g. wrong namespace, wrong checksum, etc.) then false is returned.
66
        // If the weid ends with '?', the checksum will be added
67
        // Return value is an array with the elements "oid" and "weid".
68
        // Example:
69
        //     weid2oid("weid:EXAMPLE-?").weid == "weid:EXAMPLE-3"
70
        //     weid2oid("weid:EXAMPLE-?").oid  == "1.3.6.1.4.1.37553.8.32488192274"
71
        weid2oid: function(weid) {
72
                var p = weid.lastIndexOf(':');
73
                var namespace = weid.substr(0, p+1);
74
                var rest = weid.substr(p+1);
75
 
76
                namespace = namespace.toLowerCase(); // namespace is case insensitive
77
                if (namespace == 'weid:') {
78
                        // Class C
79
                        var base = '1-3-6-1-4-1-SZ5-8';
80
                } else if (namespace == 'weid:pen:') {
81
                        // Class B
82
                        var base = '1-3-6-1-4-1';
83
                } else if (namespace == 'weid:root:') {
84
                        // Class A
85
                        var base = '';
86
                } else {
87
                        // Wrong namespace
751 daniel-mar 88
                        console.error("weid2oid: Wrong input");
750 daniel-mar 89
                        return false;
90
                }
91
 
92
                weid = rest;
93
 
94
                var elements = ((base != '') ? base.split('-') : []).concat(weid.split('-'));
95
                var actual_checksum = elements.pop();
751 daniel-mar 96
                var expected_checksum = WeidOidConverter.weLuhnCheckDigit(elements.join('-'));
750 daniel-mar 97
                if (actual_checksum != '?') {
751 daniel-mar 98
                        if (actual_checksum != expected_checksum) {
99
                                console.error("weid2oid: Wrong checksum");
100
                                return false; // wrong checksum
101
                        }
750 daniel-mar 102
                } else {
103
                        // If checksum is '?', it will be replaced by the actual checksum,
104
                        // e.g. weid:EXAMPLE-? becomes weid:EXAMPLE-3
105
                        weid = weid.replace('?', expected_checksum);
106
                }
107
                elements.forEach((o,i,a) => {
751 daniel-mar 108
                        a[i] = WeidOidConverter.base_convert_bigint(a[i], 36, 10);
750 daniel-mar 109
                });
110
                var oidstr = elements.join('.');
111
 
112
                weid = namespace.toLowerCase() + weid.toUpperCase(); // add namespace again
113
 
114
                return { "weid": weid, "oid" : oidstr };
115
        },
116
 
117
        // Converts an OID to WEID
118
        // "1.3.6.1.4.1.37553.8.32488192274" becomes "weid:EXAMPLE-3"
119
        oid2weid: function(oid) {
120
                if (oid.substr(0,1) == '.') oid = oid.substr(1); // remove leading dot
121
 
122
                if (oid != '') {
123
                        var elements = oid.split('.');
751 daniel-mar 124
                        elements.forEach((o,i,a) => {
125
                                a[i] = WeidOidConverter.base_convert_bigint(a[i], 10, 36);
750 daniel-mar 126
                        });
127
                        var weidstr = elements.join("-");
128
                } else {
129
                        var weidstr = '';
130
                }
131
 
132
                var is_class_c = (weidstr.startsWith('1-3-6-1-4-1-SZ5-8-') || (weidstr == '1-3-6-1-4-1-SZ5-8'));
133
                var is_class_b = (weidstr.startsWith('1-3-6-1-4-1-') || (weidstr == '1-3-6-1-4-1'));
134
                var is_class_a = !is_class_b && !is_class_c;
135
 
751 daniel-mar 136
                var checksum = WeidOidConverter.weLuhnCheckDigit(weidstr);
750 daniel-mar 137
 
138
                if (is_class_c) {
139
                        weidstr = weidstr.substr('1-3-6-1-4-1-SZ5-8-'.length);
140
                        var namespace = 'weid:';
141
                } else if (is_class_b) {
142
                        weidstr = weidstr.substr('1-3-6-1-4-1-'.length);
143
                        var namespace = 'weid:pen:';
144
                } else if (is_class_a) {
145
                        // weidstr stays
146
                        var namespace = 'weid:root:';
147
                } else {
148
                        // should not happen
149
                        console.error("oid2weid: Cannot detect namespace");
150
                        return false;
151
                }
152
 
751 daniel-mar 153
                return { "weid": namespace + (weidstr == '' ? checksum : weidstr + '-' + checksum), "oid": oid };
154
        },
155
 
156
        base_convert_bigint: function(numstring, frombase, tobase) {
157
 
752 daniel-mar 158
                // This variant would require the "mikemcl/bignumber.js" library:
751 daniel-mar 159
                //var x = BigNumber(numstr, frombase);
752 daniel-mar 160
                //return isNaN(x) ? false : x.toString(tobase).toUpperCase();
751 daniel-mar 161
 
162
                var frombase_str = '';
163
                for (var i=0; i<frombase; i++) {
164
                        frombase_str += parseInt(i, 10).toString(36).toUpperCase();
165
                }
166
 
167
                var tobase_str = '';
168
                for (var i=0; i<tobase; i++) {
169
                        tobase_str += parseInt(i, 10).toString(36).toUpperCase();
170
                }
171
 
752 daniel-mar 172
        for (var i=0; i<numstring.length; i++) {
173
            if (frombase_str.toLowerCase().indexOf(numstring.substr(i,1).toLowerCase()) < 0) {
174
                console.error("base_convert_bigint: Invalid input");
175
                return false;
176
            }
177
        }
178
 
179
        var length = numstring.length;
751 daniel-mar 180
                var result = '';
181
                var number = [];
182
                for (var i=0; i<length; i++) {
183
                        number[i] = frombase_str.toLowerCase().indexOf(numstring[i].toLowerCase());
184
                }
185
                do { // Loop until whole number is converted
186
                        var divide = 0;
187
                        var newlen = 0;
188
                        for (var i=0; i<length; i++) { // Perform division manually (which is why this works with big numbers)
189
                                divide = divide * frombase + parseInt(number[i]);
190
                                if (divide >= tobase) {
191
                                        number[newlen++] = (divide / tobase);
192
                                        divide = divide % tobase;
193
                                } else if (newlen > 0) {
194
                                        number[newlen++] = 0;
195
                                }
196
                        }
197
                        length = newlen;
198
                        result = tobase_str.substr(divide,1) + result; // Divide is basically numstring % tobase (i.e. the new character)
199
                }
200
                while (newlen != 0);
201
 
202
                return result;
750 daniel-mar 203
        }
204
}