Subversion Repositories oidplus

Rev

Rev 751 | Rev 754 | Go to most recent revision | View as "text/javascript" | Blame | Compare with Previous | Last modification | View Log | RSS feed

  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!
  45.                 if (!wrkstr.match(/^\d+$/)) {
  46.                         console.error("weLuhnCheckDigit: Invalid input");
  47.                         return false;
  48.                 }
  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
  88.                         console.error("weid2oid: Wrong input");
  89.                         return false;
  90.                 }
  91.  
  92.                 weid = rest;
  93.  
  94.                 var elements = ((base != '') ? base.split('-') : []).concat(weid.split('-'));
  95.                 var actual_checksum = elements.pop();
  96.                 var expected_checksum = WeidOidConverter.weLuhnCheckDigit(elements.join('-'));
  97.                 if (actual_checksum != '?') {
  98.                         if (actual_checksum != expected_checksum) {
  99.                                 console.error("weid2oid: Wrong checksum");
  100.                                 return false; // wrong checksum
  101.                         }
  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) => {
  108.                         a[i] = WeidOidConverter.base_convert_bigint(a[i], 36, 10);
  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('.');
  124.                         elements.forEach((o,i,a) => {
  125.                                 a[i] = WeidOidConverter.base_convert_bigint(a[i], 10, 36);
  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.  
  136.                 var checksum = WeidOidConverter.weLuhnCheckDigit(weidstr);
  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.  
  153.                 return { "weid": namespace + (weidstr == '' ? checksum : weidstr + '-' + checksum), "oid": oid };
  154.         },
  155.  
  156.         base_convert_bigint: function(numstring, frombase, tobase) {
  157.  
  158.                 // This variant would require the "mikemcl/bignumber.js" library:
  159.                 //var x = BigNumber(numstr, frombase);
  160.                 //return isNaN(x) ? false : x.toString(tobase).toUpperCase();
  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.  
  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;
  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;
  203.         }
  204. }
  205.