1 /*! asn1hex-1.1.6.js (c) 2012-2015 Kenji Urushima | kjur.github.com/jsrsasign/license 2 */ 3 /* 4 * asn1hex.js - Hexadecimal represented ASN.1 string library 5 * 6 * Copyright (c) 2010-2015 Kenji Urushima (kenji.urushima@gmail.com) 7 * 8 * This software is licensed under the terms of the MIT License. 9 * http://kjur.github.com/jsrsasign/license/ 10 * 11 * The above copyright and license notice shall be 12 * included in all copies or substantial portions of the Software. 13 */ 14 15 /** 16 * @fileOverview 17 * @name asn1hex-1.1.js 18 * @author Kenji Urushima kenji.urushima@gmail.com 19 * @version asn1hex 1.1.6 (2015-Jun-11) 20 * @license <a href="http://kjur.github.io/jsrsasign/license/">MIT License</a> 21 */ 22 23 /* 24 * MEMO: 25 * f('3082025b02...', 2) ... 82025b ... 3bytes 26 * f('020100', 2) ... 01 ... 1byte 27 * f('0203001...', 2) ... 03 ... 1byte 28 * f('02818003...', 2) ... 8180 ... 2bytes 29 * f('3080....0000', 2) ... 80 ... -1 30 * 31 * Requirements: 32 * - ASN.1 type octet length MUST be 1. 33 * (i.e. ASN.1 primitives like SET, SEQUENCE, INTEGER, OCTETSTRING ...) 34 */ 35 36 /** 37 * ASN.1 DER encoded hexadecimal string utility class 38 * @name ASN1HEX 39 * @class ASN.1 DER encoded hexadecimal string utility class 40 * @since jsrsasign 1.1 41 */ 42 var ASN1HEX = new function() { 43 /** 44 * get byte length for ASN.1 L(length) bytes 45 * @name getByteLengthOfL_AtObj 46 * @memberOf ASN1HEX 47 * @function 48 * @param {String} s hexadecimal string of ASN.1 DER encoded data 49 * @param {Number} pos string index 50 * @return byte length for ASN.1 L(length) bytes 51 */ 52 this.getByteLengthOfL_AtObj = function(s, pos) { 53 if (s.substring(pos + 2, pos + 3) != '8') return 1; 54 var i = parseInt(s.substring(pos + 3, pos + 4)); 55 if (i == 0) return -1; // length octet '80' indefinite length 56 if (0 < i && i < 10) return i + 1; // including '8?' octet; 57 return -2; // malformed format 58 }; 59 60 /** 61 * get hexadecimal string for ASN.1 L(length) bytes 62 * @name getHexOfL_AtObj 63 * @memberOf ASN1HEX 64 * @function 65 * @param {String} s hexadecimal string of ASN.1 DER encoded data 66 * @param {Number} pos string index 67 * @return {String} hexadecimal string for ASN.1 L(length) bytes 68 */ 69 this.getHexOfL_AtObj = function(s, pos) { 70 var len = this.getByteLengthOfL_AtObj(s, pos); 71 if (len < 1) return ''; 72 return s.substring(pos + 2, pos + 2 + len * 2); 73 }; 74 75 // getting ASN.1 length value at the position 'idx' of 76 // hexa decimal string 's'. 77 // 78 // f('3082025b02...', 0) ... 82025b ... ??? 79 // f('020100', 0) ... 01 ... 1 80 // f('0203001...', 0) ... 03 ... 3 81 // f('02818003...', 0) ... 8180 ... 128 82 /** 83 * get integer value of ASN.1 length for ASN.1 data 84 * @name getIntOfL_AtObj 85 * @memberOf ASN1HEX 86 * @function 87 * @param {String} s hexadecimal string of ASN.1 DER encoded data 88 * @param {Number} pos string index 89 * @return ASN.1 L(length) integer value 90 */ 91 this.getIntOfL_AtObj = function(s, pos) { 92 var hLength = this.getHexOfL_AtObj(s, pos); 93 if (hLength == '') return -1; 94 var bi; 95 if (parseInt(hLength.substring(0, 1)) < 8) { 96 bi = new BigInteger(hLength, 16); 97 } else { 98 bi = new BigInteger(hLength.substring(2), 16); 99 } 100 return bi.intValue(); 101 }; 102 103 /** 104 * get ASN.1 value starting string position for ASN.1 object refered by index 'idx'. 105 * @name getStartPosOfV_AtObj 106 * @memberOf ASN1HEX 107 * @function 108 * @param {String} s hexadecimal string of ASN.1 DER encoded data 109 * @param {Number} pos string index 110 */ 111 this.getStartPosOfV_AtObj = function(s, pos) { 112 var l_len = this.getByteLengthOfL_AtObj(s, pos); 113 if (l_len < 0) return l_len; 114 return pos + (l_len + 1) * 2; 115 }; 116 117 /** 118 * get hexadecimal string of ASN.1 V(value) 119 * @name getHexOfV_AtObj 120 * @memberOf ASN1HEX 121 * @function 122 * @param {String} s hexadecimal string of ASN.1 DER encoded data 123 * @param {Number} pos string index 124 * @return {String} hexadecimal string of ASN.1 value. 125 */ 126 this.getHexOfV_AtObj = function(s, pos) { 127 var pos1 = this.getStartPosOfV_AtObj(s, pos); 128 var len = this.getIntOfL_AtObj(s, pos); 129 return s.substring(pos1, pos1 + len * 2); 130 }; 131 132 /** 133 * get hexadecimal string of ASN.1 TLV at 134 * @name getHexOfTLV_AtObj 135 * @memberOf ASN1HEX 136 * @function 137 * @param {String} s hexadecimal string of ASN.1 DER encoded data 138 * @param {Number} pos string index 139 * @return {String} hexadecimal string of ASN.1 TLV. 140 * @since 1.1 141 */ 142 this.getHexOfTLV_AtObj = function(s, pos) { 143 var hT = s.substr(pos, 2); 144 var hL = this.getHexOfL_AtObj(s, pos); 145 var hV = this.getHexOfV_AtObj(s, pos); 146 return hT + hL + hV; 147 }; 148 149 /** 150 * get next sibling starting index for ASN.1 object string 151 * @name getPosOfNextSibling_AtObj 152 * @memberOf ASN1HEX 153 * @function 154 * @param {String} s hexadecimal string of ASN.1 DER encoded data 155 * @param {Number} pos string index 156 * @return next sibling starting index for ASN.1 object string 157 */ 158 this.getPosOfNextSibling_AtObj = function(s, pos) { 159 var pos1 = this.getStartPosOfV_AtObj(s, pos); 160 var len = this.getIntOfL_AtObj(s, pos); 161 return pos1 + len * 2; 162 }; 163 164 /** 165 * get array of indexes of child ASN.1 objects 166 * @name getPosArrayOfChildren_AtObj 167 * @memberOf ASN1HEX 168 * @function 169 * @param {String} s hexadecimal string of ASN.1 DER encoded data 170 * @param {Number} start string index of ASN.1 object 171 * @return {Array of Number} array of indexes for childen of ASN.1 objects 172 */ 173 this.getPosArrayOfChildren_AtObj = function(h, pos) { 174 var a = new Array(); 175 var p0 = this.getStartPosOfV_AtObj(h, pos); 176 a.push(p0); 177 178 var len = this.getIntOfL_AtObj(h, pos); 179 var p = p0; 180 var k = 0; 181 while (1) { 182 var pNext = this.getPosOfNextSibling_AtObj(h, p); 183 if (pNext == null || (pNext - p0 >= (len * 2))) break; 184 if (k >= 200) break; 185 186 a.push(pNext); 187 p = pNext; 188 189 k++; 190 } 191 192 return a; 193 }; 194 195 /** 196 * get string index of nth child object of ASN.1 object refered by h, idx 197 * @name getNthChildIndex_AtObj 198 * @memberOf ASN1HEX 199 * @function 200 * @param {String} h hexadecimal string of ASN.1 DER encoded data 201 * @param {Number} idx start string index of ASN.1 object 202 * @param {Number} nth for child 203 * @return {Number} string index of nth child. 204 * @since 1.1 205 */ 206 this.getNthChildIndex_AtObj = function(h, idx, nth) { 207 var a = this.getPosArrayOfChildren_AtObj(h, idx); 208 return a[nth]; 209 }; 210 211 // ========== decendant methods ============================== 212 /** 213 * get string index of nth child object of ASN.1 object refered by h, idx 214 * @name getDecendantIndexByNthList 215 * @memberOf ASN1HEX 216 * @function 217 * @param {String} h hexadecimal string of ASN.1 DER encoded data 218 * @param {Number} currentIndex start string index of ASN.1 object 219 * @param {Array of Number} nthList array list of nth 220 * @return {Number} string index refered by nthList 221 * @since 1.1 222 * @example 223 * The "nthList" is a index list of structured ASN.1 object 224 * reference. Here is a sample structure and "nthList"s which 225 * refers each objects. 226 * 227 * SQUENCE - 228 * SEQUENCE - [0] 229 * IA5STRING 000 - [0, 0] 230 * UTF8STRING 001 - [0, 1] 231 * SET - [1] 232 * IA5STRING 010 - [1, 0] 233 * UTF8STRING 011 - [1, 1] 234 */ 235 this.getDecendantIndexByNthList = function(h, currentIndex, nthList) { 236 if (nthList.length == 0) { 237 return currentIndex; 238 } 239 var firstNth = nthList.shift(); 240 var a = this.getPosArrayOfChildren_AtObj(h, currentIndex); 241 return this.getDecendantIndexByNthList(h, a[firstNth], nthList); 242 }; 243 244 /** 245 * get hexadecimal string of ASN.1 TLV refered by current index and nth index list. 246 * @name getDecendantHexTLVByNthList 247 * @memberOf ASN1HEX 248 * @function 249 * @param {String} h hexadecimal string of ASN.1 DER encoded data 250 * @param {Number} currentIndex start string index of ASN.1 object 251 * @param {Array of Number} nthList array list of nth 252 * @return {Number} hexadecimal string of ASN.1 TLV refered by nthList 253 * @since 1.1 254 */ 255 this.getDecendantHexTLVByNthList = function(h, currentIndex, nthList) { 256 var idx = this.getDecendantIndexByNthList(h, currentIndex, nthList); 257 return this.getHexOfTLV_AtObj(h, idx); 258 }; 259 260 /** 261 * get hexadecimal string of ASN.1 V refered by current index and nth index list. 262 * @name getDecendantHexVByNthList 263 * @memberOf ASN1HEX 264 * @function 265 * @param {String} h hexadecimal string of ASN.1 DER encoded data 266 * @param {Number} currentIndex start string index of ASN.1 object 267 * @param {Array of Number} nthList array list of nth 268 * @return {Number} hexadecimal string of ASN.1 V refered by nthList 269 * @since 1.1 270 */ 271 this.getDecendantHexVByNthList = function(h, currentIndex, nthList) { 272 var idx = this.getDecendantIndexByNthList(h, currentIndex, nthList); 273 return this.getHexOfV_AtObj(h, idx); 274 }; 275 }; 276 277 /* 278 * @since asn1hex 1.1.4 279 */ 280 ASN1HEX.getVbyList = function(h, currentIndex, nthList, checkingTag) { 281 var idx = this.getDecendantIndexByNthList(h, currentIndex, nthList); 282 if (idx === undefined) { 283 throw "can't find nthList object"; 284 } 285 if (checkingTag !== undefined) { 286 if (h.substr(idx, 2) != checkingTag) { 287 throw "checking tag doesn't match: " + 288 h.substr(idx,2) + "!=" + checkingTag; 289 } 290 } 291 return this.getHexOfV_AtObj(h, idx); 292 }; 293 294 /** 295 * get OID string from hexadecimal encoded value 296 * @name hextooidstr 297 * @memberOf ASN1HEX 298 * @function 299 * @param {String} hex hexadecmal string of ASN.1 DER encoded OID value 300 * @return {String} OID string (ex. '1.2.3.4.567') 301 * @since asn1hex 1.1.5 302 */ 303 ASN1HEX.hextooidstr = function(hex) { 304 var zeroPadding = function(s, len) { 305 if (s.length >= len) return s; 306 return new Array(len - s.length + 1).join('0') + s; 307 }; 308 309 var a = []; 310 311 // a[0], a[1] 312 var hex0 = hex.substr(0, 2); 313 var i0 = parseInt(hex0, 16); 314 a[0] = new String(Math.floor(i0 / 40)); 315 a[1] = new String(i0 % 40); 316 317 // a[2]..a[n] 318 var hex1 = hex.substr(2); 319 var b = []; 320 for (var i = 0; i < hex1.length / 2; i++) { 321 b.push(parseInt(hex1.substr(i * 2, 2), 16)); 322 } 323 var c = []; 324 var cbin = ""; 325 for (var i = 0; i < b.length; i++) { 326 if (b[i] & 0x80) { 327 cbin = cbin + zeroPadding((b[i] & 0x7f).toString(2), 7); 328 } else { 329 cbin = cbin + zeroPadding((b[i] & 0x7f).toString(2), 7); 330 c.push(new String(parseInt(cbin, 2))); 331 cbin = ""; 332 } 333 } 334 335 var s = a.join("."); 336 if (c.length > 0) s = s + "." + c.join("."); 337 return s; 338 }; 339 340 /** 341 * get string of simple ASN.1 dump from hexadecimal ASN.1 data 342 * @name dump 343 * @memberOf ASN1HEX 344 * @function 345 * @param {String} hex hexadecmal string of ASN.1 data 346 * @param {Array} associative array of flags for dump (OPTION) 347 * @param {Number} idx string index for starting dump (OPTION) 348 * @param {String} indent string (OPTION) 349 * @return {String} string of simple ASN.1 dump 350 * @since jsrsasign 4.8.3 asn1hex 1.1.6 351 * @description 352 * This method will get an ASN.1 dump from 353 * hexadecmal string of ASN.1 DER encoded data. 354 * Here are features: 355 * <ul> 356 * <li>ommit long hexadecimal string</li> 357 * <li>dump encapsulated OCTET STRING (good for X.509v3 extensions)</li> 358 * <li>structured/primitive context specific tag support (i.e. [0], [3] ...)</li> 359 * <li>automatic decode for implicit primitive context specific tag 360 * (good for X.509v3 extension value) 361 * <ul> 362 * <li>if hex starts '68747470'(i.e. http) it is decoded as utf8 encoded string.</li> 363 * <li>if it is in 'subjectAltName' extension value and is '[2]'(dNSName) tag 364 * value will be encoded as utf8 string</li> 365 * <li>otherwise it shows as hexadecimal string</li> 366 * </ul> 367 * </li> 368 * </ul> 369 * @example 370 * // ASN.1 INTEGER 371 * ASN1HEX.dump('0203012345') 372 * ↓ 373 * INTEGER 012345 374 * // ASN.1 Object Identifier 375 * ASN1HEX.dump('06052b0e03021a') 376 * ↓ 377 * ObjectIdentifier sha1 (1 3 14 3 2 26) 378 * // ASN.1 SEQUENCE 379 * ASN1HEX.dump('3006020101020102') 380 * ↓ 381 * SEQUENCE 382 * INTEGER 01 383 * INTEGER 02 384 * // ASN.1 DUMP FOR X.509 CERTIFICATE 385 * ASN1HEX.dump(X509.pemToHex(certPEM)) 386 * ↓ 387 * SEQUENCE 388 * SEQUENCE 389 * [0] 390 * INTEGER 02 391 * INTEGER 0c009310d206dbe337553580118ddc87 392 * SEQUENCE 393 * ObjectIdentifier SHA256withRSA (1 2 840 113549 1 1 11) 394 * NULL 395 * SEQUENCE 396 * SET 397 * SEQUENCE 398 * ObjectIdentifier countryName (2 5 4 6) 399 * PrintableString 'US' 400 * : 401 */ 402 ASN1HEX.dump = function(hex, flags, idx, indent) { 403 var _skipLongHex = function(hex, limitNumOctet) { 404 if (hex.length <= limitNumOctet * 2) { 405 return hex; 406 } else { 407 var s = hex.substr(0, limitNumOctet) + 408 "..(total " + hex.length / 2 + "bytes).." + 409 hex.substr(hex.length - limitNumOctet, limitNumOctet); 410 return s; 411 }; 412 }; 413 414 if (flags === undefined) flags = { "ommit_long_octet": 32 }; 415 if (idx === undefined) idx = 0; 416 if (indent === undefined) indent = ""; 417 var skipLongHex = flags.ommit_long_octet; 418 419 if (hex.substr(idx, 2) == "01") { 420 var v = ASN1HEX.getHexOfV_AtObj(hex, idx); 421 if (v == "00") { 422 return indent + "BOOLEAN FALSE\n"; 423 } else { 424 return indent + "BOOLEAN TRUE\n"; 425 } 426 } 427 if (hex.substr(idx, 2) == "02") { 428 var v = ASN1HEX.getHexOfV_AtObj(hex, idx); 429 return indent + "INTEGER " + _skipLongHex(v, skipLongHex) + "\n"; 430 } 431 if (hex.substr(idx, 2) == "03") { 432 var v = ASN1HEX.getHexOfV_AtObj(hex, idx); 433 return indent + "BITSTRING " + _skipLongHex(v, skipLongHex) + "\n"; 434 } 435 if (hex.substr(idx, 2) == "04") { 436 var v = ASN1HEX.getHexOfV_AtObj(hex, idx); 437 if (ASN1HEX.isASN1HEX(v)) { 438 var s = indent + "OCTETSTRING, encapsulates\n"; 439 s = s + ASN1HEX.dump(v, flags, 0, indent + " "); 440 return s; 441 } else { 442 return indent + "OCTETSTRING " + _skipLongHex(v, skipLongHex) + "\n"; 443 } 444 } 445 if (hex.substr(idx, 2) == "05") { 446 return indent + "NULL\n"; 447 } 448 if (hex.substr(idx, 2) == "06") { 449 var hV = ASN1HEX.getHexOfV_AtObj(hex, idx); 450 var oidDot = KJUR.asn1.ASN1Util.oidHexToInt(hV); 451 var oidName = KJUR.asn1.x509.OID.oid2name(oidDot); 452 var oidSpc = oidDot.replace(/\./g, ' '); 453 if (oidName != '') { 454 return indent + "ObjectIdentifier " + oidName + " (" + oidSpc + ")\n"; 455 } else { 456 return indent + "ObjectIdentifier (" + oidSpc + ")\n"; 457 } 458 } 459 if (hex.substr(idx, 2) == "0c") { 460 return indent + "UTF8String '" + hextoutf8(ASN1HEX.getHexOfV_AtObj(hex, idx)) + "'\n"; 461 } 462 if (hex.substr(idx, 2) == "13") { 463 return indent + "PrintableString '" + hextoutf8(ASN1HEX.getHexOfV_AtObj(hex, idx)) + "'\n"; 464 } 465 if (hex.substr(idx, 2) == "14") { 466 return indent + "TeletexString '" + hextoutf8(ASN1HEX.getHexOfV_AtObj(hex, idx)) + "'\n"; 467 } 468 if (hex.substr(idx, 2) == "16") { 469 return indent + "IA5String '" + hextoutf8(ASN1HEX.getHexOfV_AtObj(hex, idx)) + "'\n"; 470 } 471 if (hex.substr(idx, 2) == "17") { 472 return indent + "UTCTime " + hextoutf8(ASN1HEX.getHexOfV_AtObj(hex, idx)) + "\n"; 473 } 474 if (hex.substr(idx, 2) == "18") { 475 return indent + "GeneralizedTime " + hextoutf8(ASN1HEX.getHexOfV_AtObj(hex, idx)) + "\n"; 476 } 477 if (hex.substr(idx, 2) == "30") { 478 if (hex.substr(idx, 4) == "3000") { 479 return indent + "SEQUENCE {}\n"; 480 } 481 482 var s = indent + "SEQUENCE\n"; 483 var aIdx = ASN1HEX.getPosArrayOfChildren_AtObj(hex, idx); 484 485 var flagsTemp = flags; 486 487 if ((aIdx.length == 2 || aIdx.length == 3) && 488 hex.substr(aIdx[0], 2) == "06" && 489 hex.substr(aIdx[aIdx.length - 1], 2) == "04") { // supposed X.509v3 extension 490 var oidHex = ASN1HEX.getHexOfV_AtObj(hex, aIdx[0]); 491 var oidDot = KJUR.asn1.ASN1Util.oidHexToInt(oidHex); 492 var oidName = KJUR.asn1.x509.OID.oid2name(oidDot); 493 494 var flagsClone = JSON.parse(JSON.stringify(flags)); 495 flagsClone.x509ExtName = oidName; 496 flagsTemp = flagsClone; 497 } 498 499 for (var i = 0; i < aIdx.length; i++) { 500 s = s + ASN1HEX.dump(hex, flagsTemp, aIdx[i], indent + " "); 501 } 502 return s; 503 } 504 if (hex.substr(idx, 2) == "31") { 505 var s = indent + "SET\n"; 506 var aIdx = ASN1HEX.getPosArrayOfChildren_AtObj(hex, idx); 507 for (var i = 0; i < aIdx.length; i++) { 508 s = s + ASN1HEX.dump(hex, flags, aIdx[i], indent + " "); 509 } 510 return s; 511 } 512 var tag = parseInt(hex.substr(idx, 2), 16); 513 if ((tag & 128) != 0) { // context specific 514 var tagNumber = tag & 31; 515 if ((tag & 32) != 0) { // structured tag 516 var s = indent + "[" + tagNumber + "]\n"; 517 var aIdx = ASN1HEX.getPosArrayOfChildren_AtObj(hex, idx); 518 for (var i = 0; i < aIdx.length; i++) { 519 s = s + ASN1HEX.dump(hex, flags, aIdx[i], indent + " "); 520 } 521 return s; 522 } else { // primitive tag 523 var v = ASN1HEX.getHexOfV_AtObj(hex, idx); 524 if (v.substr(0, 8) == "68747470") { // http 525 v = hextoutf8(v); 526 } 527 if (flags.x509ExtName === "subjectAltName" && 528 tagNumber == 2) { 529 v = hextoutf8(v); 530 } 531 532 var s = indent + "[" + tagNumber + "] " + v + "\n"; 533 return s; 534 } 535 } 536 return indent + "UNKNOWN(" + hex.substr(idx, 2) + ") " + ASN1HEX.getHexOfV_AtObj(hex, idx) + "\n"; 537 }; 538 539 /** 540 * check wheather the string is ASN.1 hexadecimal string or not 541 * @name isASN1HEX 542 * @memberOf ASN1HEX 543 * @function 544 * @param {String} hex string to check whether it is hexadecmal string for ASN.1 DER or not 545 * @return {Boolean} true if it is hexadecimal string of ASN.1 data otherwise false 546 * @since jsrsasign 4.8.3 asn1hex 1.1.6 547 * @description 548 * This method checks wheather the argument 'hex' is a hexadecimal string of 549 * ASN.1 data or not. 550 * @example 551 * ASN1HEX.isASN1HEX('0203012345') → true // PROPER ASN.1 INTEGER 552 * ASN1HEX.isASN1HEX('0203012345ff') → false // TOO LONG VALUE 553 * ASN1HEX.isASN1HEX('02030123') → false // TOO SHORT VALUE 554 * ASN1HEX.isASN1HEX('fa3bcd') → false // WRONG FOR ASN.1 555 */ 556 ASN1HEX.isASN1HEX = function(hex) { 557 if (hex.length % 2 == 1) return false; 558 559 var intL = ASN1HEX.getIntOfL_AtObj(hex, 0); 560 var tV = hex.substr(0, 2); 561 var lV = ASN1HEX.getHexOfL_AtObj(hex, 0); 562 var hVLength = hex.length - tV.length - lV.length; 563 if (hVLength == intL * 2) return true; 564 565 return false; 566 }; 567