1 /* asn1hex-1.2.15.js (c) 2012-2022 Kenji Urushima | kjur.github.io/jsrsasign/license 2 */ 3 /* 4 * asn1hex.js - Hexadecimal represented ASN.1 string library 5 * 6 * Copyright (c) 2010-2022 Kenji Urushima (kenji.urushima@gmail.com) 7 * 8 * This software is licensed under the terms of the MIT License. 9 * https://kjur.github.io/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 jsrsasign 10.5.23 asn1hex 1.2.15 (2022-May-27) 20 * @license <a href="https://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 * @description 42 * This class provides a parser for hexadecimal string of 43 * DER encoded ASN.1 binary data. 44 * Here are major methods of this class. 45 * <ul> 46 * <li><b>ACCESS BY POSITION</b> 47 * <ul> 48 * <li>{@link ASN1HEX.getTLV} - get ASN.1 TLV at specified position</li> 49 * <li>{@link ASN1HEX.getTLVblen} - get byte length of ASN.1 TLV at specified position</li> 50 * <li>{@link ASN1HEX.getV} - get ASN.1 V at specified position</li> 51 * <li>{@link ASN1HEX.getVblen} - get integer ASN.1 L at specified position</li> 52 * <li>{@link ASN1HEX.getVidx} - get ASN.1 V position from its ASN.1 TLV position</li> 53 * <li>{@link ASN1HEX.getL} - get hexadecimal ASN.1 L at specified position</li> 54 * <li>{@link ASN1HEX.getLblen} - get byte length for ASN.1 L(length) bytes</li> 55 * </ul> 56 * </li> 57 * <li><b>ACCESS FOR CHILD ITEM</b> 58 * <ul> 59 * <li>{@link ASN1HEX.getNthChildIdx} - get nth child index at specified position</li> 60 * <li>{@link ASN1HEX.getChildIdx} - get indexes of children</li> 61 * <li>{@link ASN1HEX.getNextSiblingIdx} - get position of next sibling (DEPRECATED)</li> 62 * </ul> 63 * </li> 64 * <li><b>ACCESS NESTED ASN.1 STRUCTURE</b> 65 * <ul> 66 * <li>{@link ASN1HEX.getTLVbyList} - get ASN.1 TLV at specified list index</li> 67 * <li>{@link ASN1HEX.getVbyList} - get ASN.1 V at specified nth list index with checking expected tag</li> 68 * <li>{@link ASN1HEX.getIdxbyList} - get index at specified list index</li> 69 * </ul> 70 * </li> 71 * <li><b>(NEW)ACCESS NESTED ASN.1 STRUCTURE</b> 72 * <ul> 73 * <li>{@link ASN1HEX.getTLVbyListEx} - get ASN.1 TLV at specified list index</li> 74 * <li>{@link ASN1HEX.getVbyListEx} - get ASN.1 V at specified nth list index with checking expected tag</li> 75 * <li>{@link ASN1HEX.getIdxbyListEx} - get index at specified list index</li> 76 * </ul> 77 * </li> 78 * <li><b>UTILITIES</b> 79 * <ul> 80 * <li>{@link ASN1HEX.dump} - dump ASN.1 structure</li> 81 * <li>{@link ASN1HEX.isContextTag} - check if a hexadecimal tag is a specified ASN.1 context specific tag</li> 82 * <li>{@link ASN1HEX.isASN1HEX} - simple ASN.1 DER hexadecimal string checker</li> 83 * <li>{@link ASN1HEX.checkStrictDER} - strict ASN.1 DER hexadecimal string checker</li> 84 * <li>{@link ASN1HEX.hextooidstr} - convert hexadecimal string of OID to dotted integer list</li> 85 * </ul> 86 * </li> 87 * </ul> 88 */ 89 var ASN1HEX = new function() { 90 }; 91 92 /** 93 * get byte length for ASN.1 L(length) bytes<br/> 94 * @name getLblen 95 * @memberOf ASN1HEX 96 * @function 97 * @param {String} s hexadecimal string of ASN.1 DER encoded data 98 * @param {Number} idx string index 99 * @return byte length for ASN.1 L(length) bytes 100 * @since jsrsasign 7.2.0 asn1hex 1.1.11 101 * @example 102 * ASN1HEX.getLblen('020100', 0) → 1 for '01' 103 * ASN1HEX.getLblen('020200', 0) → 1 for '02' 104 * ASN1HEX.getLblen('02818003...', 0) → 2 for '8180' 105 * ASN1HEX.getLblen('0282025b03...', 0) → 3 for '82025b' 106 * ASN1HEX.getLblen('0280020100...', 0) → -1 for '80' BER indefinite length 107 * ASN1HEX.getLblen('02ffab...', 0) → -2 for malformed ASN.1 length 108 */ 109 ASN1HEX.getLblen = function(s, idx) { 110 if (s.substr(idx + 2, 1) != '8') return 1; 111 var i = parseInt(s.substr(idx + 3, 1)); 112 if (i == 0) return -1; // length octet '80' indefinite length 113 if (0 < i && i < 10) return i + 1; // including '8?' octet; 114 return -2; // malformed format 115 }; 116 117 /** 118 * get hexadecimal string for ASN.1 L(length) bytes<br/> 119 * @name getL 120 * @memberOf ASN1HEX 121 * @function 122 * @param {String} s hexadecimal string of ASN.1 DER encoded data 123 * @param {Number} idx string index to get L of ASN.1 object 124 * @return {String} hexadecimal string for ASN.1 L(length) bytes 125 * @since jsrsasign 7.2.0 asn1hex 1.1.11 126 */ 127 ASN1HEX.getL = function(s, idx) { 128 var len = ASN1HEX.getLblen(s, idx); 129 if (len < 1) return ''; 130 return s.substr(idx + 2, len * 2); 131 }; 132 133 /** 134 * get integer value of ASN.1 length for ASN.1 data<br/> 135 * @name getVblen 136 * @memberOf ASN1HEX 137 * @function 138 * @param {String} s hexadecimal string of ASN.1 DER encoded data 139 * @param {Number} idx string index 140 * @return {Number} ASN.1 L(length) integer value 141 * @since jsrsasign 7.2.0 asn1hex 1.1.11 142 */ 143 /* 144 getting ASN.1 length value at the position 'idx' of 145 hexa decimal string 's'. 146 f('3082025b02...', 0) ... 82025b ... ??? 147 f('020100', 0) ... 01 ... 1 148 f('0203001...', 0) ... 03 ... 3 149 f('02818003...', 0) ... 8180 ... 128 150 */ 151 ASN1HEX.getVblen = function(s, idx) { 152 var hLen, bi; 153 hLen = ASN1HEX.getL(s, idx); 154 if (hLen == '') return -1; 155 if (hLen.substr(0, 1) === '8') { 156 bi = new BigInteger(hLen.substr(2), 16); 157 } else { 158 bi = new BigInteger(hLen, 16); 159 } 160 return bi.intValue(); 161 }; 162 163 /** 164 * get ASN.1 value starting string position for ASN.1 object refered by index 'idx'. 165 * @name getVidx 166 * @memberOf ASN1HEX 167 * @function 168 * @param {String} s hexadecimal string of ASN.1 DER encoded data 169 * @param {Number} idx string index 170 * @since jsrsasign 7.2.0 asn1hex 1.1.11 171 */ 172 ASN1HEX.getVidx = function(s, idx) { 173 var l_len = ASN1HEX.getLblen(s, idx); 174 if (l_len < 0) return l_len; 175 return idx + (l_len + 1) * 2; 176 }; 177 178 /** 179 * get hexadecimal string of ASN.1 V(value)<br/> 180 * @name getV 181 * @memberOf ASN1HEX 182 * @function 183 * @param {String} s hexadecimal string of ASN.1 DER encoded data 184 * @param {Number} idx string index 185 * @return {String} hexadecimal string of ASN.1 value. 186 * @since jsrsasign 7.2.0 asn1hex 1.1.11 187 */ 188 ASN1HEX.getV = function(s, idx) { 189 var idx1 = ASN1HEX.getVidx(s, idx); 190 var blen = ASN1HEX.getVblen(s, idx); 191 return s.substr(idx1, blen * 2); 192 }; 193 194 /** 195 * get hexadecimal string of ASN.1 TLV at<br/> 196 * @name getTLV 197 * @memberOf ASN1HEX 198 * @function 199 * @param {String} s hexadecimal string of ASN.1 DER encoded data 200 * @param {Number} idx string index 201 * @return {String} hexadecimal string of ASN.1 TLV. 202 * @since jsrsasign 7.2.0 asn1hex 1.1.11 203 */ 204 ASN1HEX.getTLV = function(s, idx) { 205 return s.substr(idx, 2) + ASN1HEX.getL(s, idx) + ASN1HEX.getV(s, idx); 206 }; 207 208 /** 209 * get byte length of ASN.1 TLV at specified string index<br/> 210 * @name getTLVblen 211 * @memberOf ASN1HEX 212 * @function 213 * @param {String} h hexadecimal string of ASN.1 DER encoded data 214 * @param {Number} idx string index to get ASN.1 TLV byte length 215 * @return {Number} byte length of ASN.1 TLV 216 * @since jsrsasign 9.1.5 asn1hex 1.1.11 217 * 218 * @description 219 * This method returns a byte length of ASN.1 TLV at 220 * specified string index. 221 * 222 * @example 223 * v string indx=42 224 * ASN1HEX.getTLVblen("...1303616161...", 42) → 10 (PrintableString 'aaa') 225 */ 226 ASN1HEX.getTLVblen = function(h, idx) { 227 return 2 + ASN1HEX.getLblen(h, idx) * 2 + ASN1HEX.getVblen(h, idx) * 2; 228 }; 229 230 // ========== sibling methods ================================ 231 232 /** 233 * get next sibling starting index for ASN.1 object string (DEPRECATED)<br/> 234 * @name getNextSiblingIdx 235 * @memberOf ASN1HEX 236 * @function 237 * @param {String} s hexadecimal string of ASN.1 DER encoded data 238 * @param {Number} idx string index 239 * @return {Number} next sibling starting index for ASN.1 object string 240 * @since jsrsasign 7.2.0 asn1hex 1.1.11 241 * @deprecated jsrsasign 9.1.5 asn1hex 1.2.5 Please use {@link ASN1HEX.getTLVblen} 242 * 243 * @example 244 * SEQUENCE { INTEGER 3, INTEGER 4 } 245 * 3006 246 * 020103 :idx=4 247 * 020104 :next sibling idx=10 248 * getNextSiblingIdx("3006020103020104", 4) & rarr 10 249 */ 250 ASN1HEX.getNextSiblingIdx = function(s, idx) { 251 var idx1 = ASN1HEX.getVidx(s, idx); 252 var blen = ASN1HEX.getVblen(s, idx); 253 return idx1 + blen * 2; 254 }; 255 256 // ========== children methods =============================== 257 /** 258 * get array of string indexes of child ASN.1 objects<br/> 259 * @name getChildIdx 260 * @memberOf ASN1HEX 261 * @function 262 * @param {String} h hexadecimal string of ASN.1 DER encoded data 263 * @param {Number} idx start string index of ASN.1 object 264 * @return {Array of Number} array of indexes for childen of ASN.1 objects 265 * @since jsrsasign 7.2.0 asn1hex 1.1.11 266 * @description 267 * This method returns array of integers for a concatination of ASN.1 objects 268 * in a ASN.1 value. As for BITSTRING, one byte of unusedbits is skipped. 269 * As for other ASN.1 simple types such as INTEGER, OCTET STRING or PRINTABLE STRING, 270 * it returns a array of a string index of its ASN.1 value.<br/> 271 * NOTE: Since asn1hex 1.1.7 of jsrsasign 6.1.2, Encapsulated BitString is supported. 272 * @example 273 * ASN1HEX.getChildIdx("0203012345", 0) ⇒ [4] // INTEGER 012345 274 * ASN1HEX.getChildIdx("1303616161", 0) ⇒ [4] // PrintableString aaa 275 * ASN1HEX.getChildIdx("030300ffff", 0) ⇒ [6] // BITSTRING ffff (unusedbits=00a) 276 * ASN1HEX.getChildIdx("3006020104020105", 0) ⇒ [4, 10] // SEQUENCE(INT4,INT5) 277 * ASN1HEX.getChildIdx("30841084107730030101ff", 0) ⇒ raise error for lacking vaule 278 */ 279 ASN1HEX.getChildIdx = function(h, idx) { 280 var _ASN1HEX = ASN1HEX; 281 var a = []; 282 var idxStart, totalChildBlen, currentChildBlen; 283 284 idxStart = _ASN1HEX.getVidx(h, idx); 285 totalChildBlen = _ASN1HEX.getVblen(h, idx) * 2; 286 287 if (idxStart + totalChildBlen > h.length) { 288 throw new Error("too short ASN.1 value"); 289 } 290 291 if (h.substr(idx, 2) == "03") { // BITSTRING without unusedbits 292 idxStart += 2; 293 totalChildBlen -= 2; 294 } 295 296 currentChildBlen = 0; 297 var i = idxStart; 298 while (currentChildBlen <= totalChildBlen) { 299 var tlvBlen = _ASN1HEX.getTLVblen(h, i); 300 if (tlvBlen <= 0) throw new Error("malformed ASN.1: invalid TLV length"); 301 currentChildBlen += tlvBlen; 302 if (currentChildBlen <= totalChildBlen) a.push(i); 303 i += tlvBlen; 304 if (currentChildBlen >= totalChildBlen) break; 305 } 306 return a; 307 }; 308 309 /** 310 * get string index of nth child object of ASN.1 object refered by h, idx<br/> 311 * @name getNthChildIdx 312 * @memberOf ASN1HEX 313 * @function 314 * @param {String} h hexadecimal string of ASN.1 DER encoded data 315 * @param {Number} idx start string index of ASN.1 object 316 * @param {Number} nth for child 317 * @return {Number} string index of nth child. 318 * @since jsrsasign 7.2.0 asn1hex 1.1.11 319 */ 320 ASN1HEX.getNthChildIdx = function(h, idx, nth) { 321 var a = ASN1HEX.getChildIdx(h, idx); 322 return a[nth]; 323 }; 324 325 // ========== decendant methods ============================== 326 /** 327 * get string index of nth child object of ASN.1 object refered by h, idx<br/> 328 * @name getIdxbyList 329 * @memberOf ASN1HEX 330 * @function 331 * @param {String} h hexadecimal string of ASN.1 DER encoded data 332 * @param {Number} currentIndex start string index of ASN.1 object 333 * @param {Array of Number} nthList array list of nth 334 * @param {String} checkingTag (OPTIONAL) string of expected ASN.1 tag for nthList 335 * @return {Number} string index refered by nthList 336 * @since jsrsasign 7.1.4 asn1hex 1.1.10. 337 * @description 338 * @example 339 * The "nthList" is a index list of structured ASN.1 object 340 * reference. Here is a sample structure and "nthList"s which 341 * refers each objects. 342 * 343 * SQUENCE - 344 * SEQUENCE - [0] 345 * IA5STRING 000 - [0, 0] 346 * UTF8STRING 001 - [0, 1] 347 * SET - [1] 348 * IA5STRING 010 - [1, 0] 349 * UTF8STRING 011 - [1, 1] 350 */ 351 ASN1HEX.getIdxbyList = function(h, currentIndex, nthList, checkingTag) { 352 var _ASN1HEX = ASN1HEX; 353 var firstNth, a; 354 if (nthList.length == 0) { 355 if (checkingTag !== undefined) { 356 if (h.substr(currentIndex, 2) !== checkingTag) return -1; 357 } 358 return currentIndex; 359 } 360 firstNth = nthList.shift(); 361 a = _ASN1HEX.getChildIdx(h, currentIndex); 362 if (firstNth >= a.length) return -1; 363 364 return _ASN1HEX.getIdxbyList(h, a[firstNth], nthList, checkingTag); 365 }; 366 367 /** 368 * get string index of nth child object of ASN.1 object refered by h, idx<br/> 369 * @name getIdxbyListEx 370 * @memberOf ASN1HEX 371 * @function 372 * @param {String} h hexadecimal string of ASN.1 DER encoded data 373 * @param {Number} currentIndex start string index of ASN.1 object 374 * @param {Array of Object} nthList array list of nth index value or context specific tag string (ex. "[0]") 375 * @param {String} checkingTag (OPTIONAL) string of expected ASN.1 tag for nthList 376 * @return {Number} string index refered by nthList. return -1 if not found 377 * @since jsrsasign 8.0.21 asn1hex 1.2.2 378 * @see <a href="https://github.com/kjur/jsrsasign/wiki/Tutorial-for-accessing-deep-inside-of-ASN.1-structure-by-using-new-ASN1HEX.getIdxbyListEx">ASN1HEX.getIdxbyListEx tutorial wiki page</a> 379 * 380 * @description 381 * This method returns the string index in h specified by currentIndex and 382 * nthList. This is useful to dig into a deep structured ASN.1 object 383 * by indexes called nthList. 384 * <br/> 385 * A nthList consists of a position number in children of ASN.1 386 * structured data or a context specific tag string (ex. "[1]"). 387 * Here is a sample deep structured ASN.1 data and 388 * nthLists referring decendent objects. 389 * <blockquote><pre> 390 * SQUENCE - referring nthList is below: 391 * SEQUENCE - [0] 392 * IA5STRING "a1" - [0, 0] 393 * UTF8STRING "a2" - [0, 1] 394 * SET - [1] 395 * IA5STRING "b1" - [1, 0] 396 * UTF8STRING "b2" - [1, 1] 397 * [0] "b3" - [1, "[0]"] // optional since context tag 398 * [1] "b4" - [1, "[1]"] // optional since context tag 399 * IA5STRING "b5" - [1, 2] // context is skipped. next is 2 400 * UTF8STRING "b6" - [1, 3] 401 * </pre></blockquote> 402 * 403 * <br/> 404 * This method can dig into ASN.1 object encapsulated by 405 * OctetString or BitString with unused bits. 406 * 407 * @example 408 * 3014 seq idx=0 409 * 3012 seq idx=4 410 * 020101 int:1 idx=8 411 * 020102 int:2 idx=14 412 * 800103 [0]:3 idx=20 413 * 810104 [1]:4 idx=26 414 * 020105 int:5 idx=32 415 * 020106 int:6 idx=38 416 * h = "30140412020101020102800103810104020105020106"; 417 * ASN1HEX.getIdxbyListEx(h, 0, [0, "[0]"]) → 16 418 * ASN1HEX.getIdxbyListEx(h, 0, [0, 2]) → 28 419 * ASN1HEX.getIdxbyListEx(h, 0, [0, 2], "0c") → -1 //not UTF8String(0c) 420 */ 421 ASN1HEX.getIdxbyListEx = function(h, currentIndex, nthList, checkingTag) { 422 var _ASN1HEX = ASN1HEX; 423 var firstNth, a; 424 if (nthList.length == 0) { 425 if (checkingTag !== undefined) { 426 if (h.substr(currentIndex, 2) !== checkingTag) { 427 return -1; 428 } 429 } 430 return currentIndex; 431 } 432 firstNth = nthList.shift(); 433 a = _ASN1HEX.getChildIdx(h, currentIndex); 434 435 var count = 0; 436 for (var i = 0; i < a.length; i++) { 437 var childTag = h.substr(a[i], 2); 438 439 if ((typeof firstNth == "number" && 440 (! _ASN1HEX.isContextTag(childTag)) && 441 count == firstNth) || 442 (typeof firstNth == "string" && 443 _ASN1HEX.isContextTag(childTag, firstNth))) { 444 return _ASN1HEX.getIdxbyListEx(h, a[i], nthList, checkingTag); 445 } 446 if (! _ASN1HEX.isContextTag(childTag)) count++; 447 } 448 return -1; 449 }; 450 451 /** 452 * get ASN.1 TLV by nthList<br/> 453 * @name getTLVbyList 454 * @memberOf ASN1HEX 455 * @function 456 * @param {String} h hexadecimal string of ASN.1 structure 457 * @param {Integer} currentIndex string index to start searching in hexadecimal string "h" 458 * @param {Array} nthList array of nth list index 459 * @param {String} checkingTag (OPTIONAL) string of expected ASN.1 tag for nthList 460 * @return {String} referred hexadecimal string of ASN.1 TLV or null 461 * @since jsrsasign 7.1.4 asn1hex 1.1.10 462 * 463 * @description 464 * This static method is to get a ASN.1 value which specified "nthList" position 465 * with checking expected tag "checkingTag". 466 * <br/> 467 * When referring value can't be found, this returns null. 468 */ 469 ASN1HEX.getTLVbyList = function(h, currentIndex, nthList, checkingTag) { 470 var _ASN1HEX = ASN1HEX; 471 var idx = _ASN1HEX.getIdxbyList(h, currentIndex, nthList, checkingTag); 472 473 if (idx == -1) return null; 474 if (idx >= h.length) return null; 475 476 return _ASN1HEX.getTLV(h, idx); 477 }; 478 479 /** 480 * get ASN.1 TLV by nthList<br/> 481 * @name getTLVbyListEx 482 * @memberOf ASN1HEX 483 * @function 484 * @param {String} h hexadecimal string of ASN.1 structure 485 * @param {Integer} currentIndex string index to start searching in hexadecimal string "h" 486 * @param {Array of Object} nthList array list of nth index value or context specific tag string (ex. "[0]") 487 * @param {String} checkingTag (OPTIONAL) string of expected ASN.1 tag for nthList 488 * @return {String} hexadecimal ASN.1 TLV string refered by nthList. return null if not found 489 * @since jsrsasign 8.0.21 asn1hex 1.2.2 490 * @see <a href="https://github.com/kjur/jsrsasign/wiki/Tutorial-for-accessing-deep-inside-of-ASN.1-structure-by-using-new-ASN1HEX.getIdxbyListEx">ASN1HEX.getIdxbyListEx tutorial wiki page</a> 491 * @see {@link ASN1HEX.getIdxbyListEx} 492 * @description 493 * This static method is to get a ASN.1 value which specified "nthList" position 494 * with checking expected tag "checkingTag". 495 * This method can dig into ASN.1 object encapsulated by 496 * OctetString or BitString with unused bits. 497 * @example 498 * 3014 seq idx=0 499 * 0312 seq idx=4 500 * 020101 int:1 idx=8 501 * 020102 int:2 idx=14 502 * 800103 [0]:3 idx=20 503 * 810104 [1]:4 idx=26 504 * 020105 int:5 idx=32 505 * 020106 int:6 idx=38 506 * h = "30140412020101020102800103810104020105020106"; 507 * ASN1HEX.getTLVbyList(h, 0, [0, "[0]"]) → 800103 508 * ASN1HEX.getTLVbyList(h, 0, [0, 2]) → 020105 509 * ASN1HEX.getTLVbyList(h, 0, [0, 2], "0c") → null //not UTF8String(0c) 510 */ 511 ASN1HEX.getTLVbyListEx = function(h, currentIndex, nthList, checkingTag) { 512 var _ASN1HEX = ASN1HEX; 513 var idx = _ASN1HEX.getIdxbyListEx(h, currentIndex, nthList, checkingTag); 514 if (idx == -1) return null; 515 return _ASN1HEX.getTLV(h, idx); 516 }; 517 518 /** 519 * get ASN.1 value by nthList<br/> 520 * @name getVbyList 521 * @memberOf ASN1HEX 522 * @function 523 * @param {String} h hexadecimal string of ASN.1 structure 524 * @param {Integer} currentIndex string index to start searching in hexadecimal string "h" 525 * @param {Array} nthList array of nth list index 526 * @param {String} checkingTag (OPTIONAL) string of expected ASN.1 tag for nthList 527 * @param {Boolean} removeUnusedbits (OPTIONAL) flag for remove first byte for value (DEFAULT false) 528 * @return {String} referred hexadecimal string of ASN.1 value(V) or null 529 * @since asn1hex 1.1.4 530 * @see ASN1HEX.getIdxbyList 531 * @see ASN1HEX.getVbyListEx 532 * 533 * @description 534 * This static method is to get a ASN.1 value which specified "nthList" position 535 * with checking expected tag "checkingTag". 536 * <br/> 537 * When referring value can't be found, this returns null. 538 * <br/> 539 * NOTE: 'removeUnusedbits' flag has been supported since 540 * jsrsasign 7.1.14 asn1hex 1.1.10. 541 */ 542 ASN1HEX.getVbyList = function(h, currentIndex, nthList, checkingTag, removeUnusedbits) { 543 var _ASN1HEX = ASN1HEX; 544 var idx, v; 545 idx = _ASN1HEX.getIdxbyList(h, currentIndex, nthList, checkingTag); 546 547 if (idx == -1) return null; 548 if (idx >= h.length) return null; 549 550 v = _ASN1HEX.getV(h, idx); 551 if (removeUnusedbits === true) v = v.substr(2); 552 return v; 553 }; 554 555 /** 556 * get ASN.1 V by nthList<br/> 557 * @name getVbyListEx 558 * @memberOf ASN1HEX 559 * @function 560 * @param {String} h hexadecimal string of ASN.1 structure 561 * @param {Integer} currentIndex string index to start searching in hexadecimal string "h" 562 * @param {Array of Object} nthList array list of nth index value or context specific tag string (ex. "[0]") 563 * @param {String} checkingTag (OPTIONAL) string of expected ASN.1 tag for nthList (default is undefined) 564 * @param {Boolean} removeUnusedbits (OPTIONAL) flag for trim unused bit from result value (default is undefined) 565 * @return {String} hexadecimal ASN.1 V string refered by nthList. return null if not found 566 * @since jsrsasign 8.0.21 asn1hex 1.2.2 567 * @see <a href="https://github.com/kjur/jsrsasign/wiki/Tutorial-for-accessing-deep-inside-of-ASN.1-structure-by-using-new-ASN1HEX.getIdxbyListEx">ASN1HEX.getIdxbyListEx tutorial wiki page</a> 568 * @see {@link ASN1HEX.getIdxbyListEx} 569 * 570 * @description 571 * This static method is to get a ASN.1 value which specified "nthList" position 572 * with checking expected tag "checkingTag". 573 * This method can dig into ASN.1 object encapsulated by 574 * OctetString or BitString with unused bits. 575 * 576 * @example 577 * 3014 seq idx=0 578 * 3012 seq idx=4 579 * 020101 int:1 idx=8 580 * 020102 int:2 idx=14 581 * 800103 [0]:3 idx=20 582 * 810104 [1]:4 idx=26 583 * 020105 int:5 idx=32 584 * 020106 int:6 idx=38 585 * h = "30140412020101020102800103810104020105020106"; 586 * ASN1HEX.getTLVbyList(h, 0, [0, "[0]"]) → 03 587 * ASN1HEX.getTLVbyList(h, 0, [0, 2]) → 05 588 * ASN1HEX.getTLVbyList(h, 0, [0, 2], "0c") → null //not UTF8String(0c) 589 */ 590 ASN1HEX.getVbyListEx = function(h, currentIndex, nthList, checkingTag, removeUnusedbits) { 591 var _ASN1HEX = ASN1HEX; 592 var idx, tlv, v; 593 idx = _ASN1HEX.getIdxbyListEx(h, currentIndex, nthList, checkingTag); 594 if (idx == -1) return null; 595 v = _ASN1HEX.getV(h, idx); 596 if (h.substr(idx, 2) == "03" && removeUnusedbits !== false) v = v.substr(2); 597 return v; 598 }; 599 600 /** 601 * get integer value from ASN.1 V(value) of Integer or BitString<br/> 602 * @name getInt 603 * @memberOf ASN1HEX 604 * @function 605 * @param {String} h hexadecimal string 606 * @param {Number} idx string index in h to get ASN.1 DER Integer or BitString 607 * @param {Object} errorReturn (OPTION) error return value (DEFAULT: -1) 608 * @return {Number} ASN.1 DER Integer or BitString value 609 * @since jsrsasign 10.1.0 asn1hex 1.2.7 610 * @see bitstrtoint 611 * 612 * @example 613 * ASN1HEX.getInt("xxxx020103xxxxxx", 4) &rarr 3 // DER Integer 614 * ASN1HEX.getInt("xxxx03020780xxxxxx", 4) &rarr 1 // DER BitStringx 615 * ASN1HEX.getInt("xxxx030203c8xxxxxx", 4) &rarr 25 // DER BitStringx 616 */ 617 ASN1HEX.getInt = function(h, idx, errorReturn) { 618 if (errorReturn == undefined) errorReturn = -1; 619 try { 620 var hTag = h.substr(idx, 2); 621 if (hTag != "02" && hTag != "03") return errorReturn; 622 var hV = ASN1HEX.getV(h, idx); 623 if (hTag == "02") { 624 return parseInt(hV, 16); 625 } else { 626 return bitstrtoint(hV); 627 } 628 } catch(ex) { 629 return errorReturn; 630 } 631 }; 632 633 /** 634 * get object identifier string from ASN.1 V(value)<br/> 635 * @name getOID 636 * @memberOf ASN1HEX 637 * @function 638 * @param {String} h hexadecimal string 639 * @param {Number} idx string index in h to get ASN.1 DER ObjectIdentifier 640 * @param {Object} errorReturn (OPTION) error return value (DEFAULT: null) 641 * @return {String} object identifier string (ex. "1.2.3.4") 642 * @since jsrsasign 10.1.0 asn1hex 1.2.7 643 * 644 * @example 645 * ASN1HEX.getInt("xxxx06032a0304xxxxxx", 4) &rarr "1.2.3.4" 646 */ 647 ASN1HEX.getOID = function(h, idx, errorReturn) { 648 if (errorReturn == undefined) errorReturn = null; 649 try { 650 if (h.substr(idx, 2) != "06") return errorReturn; 651 var hOID = ASN1HEX.getV(h, idx); 652 return hextooid(hOID); 653 } catch(ex) { 654 return errorReturn; 655 } 656 }; 657 658 /** 659 * get object identifier name from ASN.1 V(value)<br/> 660 * @name getOIDName 661 * @memberOf ASN1HEX 662 * @function 663 * @param {String} h hexadecimal string 664 * @param {Number} idx string index in h to get ASN.1 DER ObjectIdentifier 665 * @param {Object} errorReturn (OPTION) error return value (DEFAULT: null) 666 * @return {String} object identifier name (ex. "sha256") oir OID string 667 * @since jsrsasign 10.1.0 asn1hex 1.2.7 668 * 669 * @description 670 * This static method returns object identifier name such as "sha256" 671 * if registered. If not registered, it returns OID string. 672 * (ex. "1.2.3.4") 673 * 674 * @example 675 * ASN1HEX.getOIDName("xxxx0609608648016503040201xxxxxx", 4) &rarr "sha256" 676 * ASN1HEX.getOIDName("xxxx06032a0304xxxxxx", 4) &rarr "1.2.3.4" 677 */ 678 ASN1HEX.getOIDName = function(h, idx, errorReturn) { 679 if (errorReturn == undefined) errorReturn = null; 680 try { 681 var oid = ASN1HEX.getOID(h, idx, errorReturn); 682 if (oid == errorReturn) return errorReturn; 683 var name = KJUR.asn1.x509.OID.oid2name(oid); 684 if (name == '') return oid; 685 return name; 686 } catch(ex) { 687 return errorReturn; 688 } 689 }; 690 691 /** 692 * get raw string from ASN.1 V(value)<br/> 693 * @name getString 694 * @memberOf ASN1HEX 695 * @function 696 * @param {String} h hexadecimal string 697 * @param {Number} idx string index in h to get any ASN.1 DER String 698 * @param {Object} errorReturn (OPTION) error return value (DEFAULT: null) 699 * @return {String} raw string 700 * @since jsrsasign 10.1.3 asn1hex 1.2.8 701 * 702 * @description 703 * This static method returns a raw string from 704 * any ASN.1 DER primitives. 705 * 706 * @example 707 * ASN1HEX.getString("xxxx1303616161xxxxxx", 4) &rarr "aaa" 708 * ASN1HEX.getString("xxxx0c03616161xxxxxx", 4) &rarr "aaa" 709 */ 710 ASN1HEX.getString = function(h, idx, errorReturn) { 711 if (errorReturn == undefined) errorReturn = null; 712 try { 713 var hV = ASN1HEX.getV(h, idx); 714 return hextorstr(hV); 715 } catch(ex) { 716 return errorReturn; 717 } 718 }; 719 720 /** 721 * get OID string from hexadecimal encoded value<br/> 722 * @name hextooidstr 723 * @memberOf ASN1HEX 724 * @function 725 * @param {String} hex hexadecmal string of ASN.1 DER encoded OID value 726 * @return {String} OID string (ex. '1.2.3.4.567') 727 * @since asn1hex 1.1.5 728 * @see {@link KJUR.asn1.ASN1Util.oidIntToHex} 729 * @description 730 * This static method converts from ASN.1 DER encoded 731 * hexadecimal object identifier value to dot concatinated OID value. 732 * {@link KJUR.asn1.ASN1Util.oidIntToHex} is a reverse function of this. 733 * @example 734 * ASN1HEX.hextooidstr("550406") → "2.5.4.6" 735 */ 736 ASN1HEX.hextooidstr = function(hex) { 737 var zeroPadding = function(s, len) { 738 if (s.length >= len) return s; 739 return new Array(len - s.length + 1).join('0') + s; 740 }; 741 742 var a = []; 743 744 // a[0], a[1] 745 var hex0 = hex.substr(0, 2); 746 var i0 = parseInt(hex0, 16); 747 a[0] = new String(Math.floor(i0 / 40)); 748 a[1] = new String(i0 % 40); 749 750 // a[2]..a[n] 751 var hex1 = hex.substr(2); 752 var b = []; 753 for (var i = 0; i < hex1.length / 2; i++) { 754 b.push(parseInt(hex1.substr(i * 2, 2), 16)); 755 } 756 var c = []; 757 var cbin = ""; 758 for (var i = 0; i < b.length; i++) { 759 if (b[i] & 0x80) { 760 cbin = cbin + zeroPadding((b[i] & 0x7f).toString(2), 7); 761 } else { 762 cbin = cbin + zeroPadding((b[i] & 0x7f).toString(2), 7); 763 c.push(new String(parseInt(cbin, 2))); 764 cbin = ""; 765 } 766 } 767 768 var s = a.join("."); 769 if (c.length > 0) s = s + "." + c.join("."); 770 return s; 771 }; 772 773 /** 774 * get string of simple ASN.1 dump from hexadecimal ASN.1 data<br/> 775 * @name dump 776 * @memberOf ASN1HEX 777 * @function 778 * @param {Object} hexOrObj hexadecmal string of ASN.1 data or ASN1Object object 779 * @param {Array} flags associative array of flags for dump (OPTION) 780 * @param {Number} idx string index for starting dump (OPTION) 781 * @param {String} indent indent string (OPTION) 782 * @return {String} string of simple ASN.1 dump 783 * @since jsrsasign 4.8.3 asn1hex 1.1.6 784 * @description 785 * This method will get an ASN.1 dump from 786 * hexadecmal string of ASN.1 DER encoded data. 787 * Here are features: 788 * <ul> 789 * <li>ommit long hexadecimal string</li> 790 * <li>dump encapsulated OCTET STRING (good for X.509v3 extensions)</li> 791 * <li>structured/primitive context specific tag support (i.e. [0], [3] ...)</li> 792 * <li>automatic decode for implicit primitive context specific tag 793 * (good for X.509v3 extension value) 794 * <ul> 795 * <li>if hex starts '68747470'(i.e. http) it is decoded as utf8 encoded string.</li> 796 * <li>if it is in 'subjectAltName' extension value and is '[2]'(dNSName) tag 797 * value will be encoded as utf8 string</li> 798 * <li>otherwise it shows as hexadecimal string</li> 799 * </ul> 800 * </li> 801 * </ul> 802 * NOTE1: Argument {@link KJUR.asn1.ASN1Object} object is supported since 803 * jsrsasign 6.2.4 asn1hex 1.0.8 804 * @example 805 * // 1) ASN.1 INTEGER 806 * ASN1HEX.dump('0203012345') 807 * ↓ 808 * INTEGER 012345 809 * 810 * // 2) ASN.1 Object Identifier 811 * ASN1HEX.dump('06052b0e03021a') 812 * ↓ 813 * ObjectIdentifier sha1 (1 3 14 3 2 26) 814 * 815 * // 3) ASN.1 SEQUENCE 816 * ASN1HEX.dump('3006020101020102') 817 * ↓ 818 * SEQUENCE 819 * INTEGER 01 820 * INTEGER 02 821 * 822 * // 4) ASN.1 SEQUENCE since jsrsasign 6.2.4 823 * o = KJUR.asn1.ASN1Util.newObject({seq: [{int: 1}, {int: 2}]}); 824 * ASN1HEX.dump(o) 825 * ↓ 826 * SEQUENCE 827 * INTEGER 01 828 * INTEGER 02 829 * // 5) ASN.1 DUMP FOR X.509 CERTIFICATE 830 * ASN1HEX.dump(pemtohex(certPEM)) 831 * ↓ 832 * SEQUENCE 833 * SEQUENCE 834 * [0] 835 * INTEGER 02 836 * INTEGER 0c009310d206dbe337553580118ddc87 837 * SEQUENCE 838 * ObjectIdentifier SHA256withRSA (1 2 840 113549 1 1 11) 839 * NULL 840 * SEQUENCE 841 * SET 842 * SEQUENCE 843 * ObjectIdentifier countryName (2 5 4 6) 844 * PrintableString 'US' 845 * : 846 */ 847 ASN1HEX.dump = function(hexOrObj, flags, idx, indent) { 848 var _ASN1HEX = ASN1HEX; 849 var _getV = _ASN1HEX.getV; 850 var _dump = _ASN1HEX.dump; 851 var _getChildIdx = _ASN1HEX.getChildIdx; 852 853 var hex = hexOrObj; 854 if (hexOrObj instanceof KJUR.asn1.ASN1Object) 855 hex = hexOrObj.tohex(); 856 857 var _skipLongHex = function(hex, limitNumOctet) { 858 if (hex.length <= limitNumOctet * 2) { 859 return hex; 860 } else { 861 var s = hex.substr(0, limitNumOctet) + 862 "..(total " + hex.length / 2 + "bytes).." + 863 hex.substr(hex.length - limitNumOctet, limitNumOctet); 864 return s; 865 }; 866 }; 867 868 if (flags === undefined) flags = { "ommit_long_octet": 32 }; 869 if (idx === undefined) idx = 0; 870 if (indent === undefined) indent = ""; 871 var skipLongHex = flags.ommit_long_octet; 872 873 var tag = hex.substr(idx, 2); 874 875 if (tag == "01") { 876 var v = _getV(hex, idx); 877 if (v == "00") { 878 return indent + "BOOLEAN FALSE\n"; 879 } else { 880 return indent + "BOOLEAN TRUE\n"; 881 } 882 } 883 if (tag == "02") { 884 var v = _getV(hex, idx); 885 return indent + "INTEGER " + _skipLongHex(v, skipLongHex) + "\n"; 886 } 887 if (tag == "03") { 888 var v = _getV(hex, idx); 889 if (_ASN1HEX.isASN1HEX(v.substr(2))) { 890 var s = indent + "BITSTRING, encapsulates\n"; 891 s = s + _dump(v.substr(2), flags, 0, indent + " "); 892 return s; 893 } else { 894 return indent + "BITSTRING " + _skipLongHex(v, skipLongHex) + "\n"; 895 } 896 } 897 if (tag == "04") { 898 var v = _getV(hex, idx); 899 if (_ASN1HEX.isASN1HEX(v)) { 900 var s = indent + "OCTETSTRING, encapsulates\n"; 901 s = s + _dump(v, flags, 0, indent + " "); 902 return s; 903 } else { 904 return indent + "OCTETSTRING " + _skipLongHex(v, skipLongHex) + "\n"; 905 } 906 } 907 if (tag == "05") { 908 return indent + "NULL\n"; 909 } 910 if (tag == "06") { 911 var hV = _getV(hex, idx); 912 var oidDot = KJUR.asn1.ASN1Util.oidHexToInt(hV); 913 var oidName = KJUR.asn1.x509.OID.oid2name(oidDot); 914 var oidSpc = oidDot.replace(/\./g, ' '); 915 if (oidName != '') { 916 return indent + "ObjectIdentifier " + oidName + " (" + oidSpc + ")\n"; 917 } else { 918 return indent + "ObjectIdentifier (" + oidSpc + ")\n"; 919 } 920 } 921 if (tag == "0a") { 922 return indent + "ENUMERATED " + parseInt(_getV(hex, idx)) + "\n"; 923 } 924 if (tag == "0c") { 925 return indent + "UTF8String '" + hextoutf8(_getV(hex, idx)) + "'\n"; 926 } 927 if (tag == "13") { 928 return indent + "PrintableString '" + hextoutf8(_getV(hex, idx)) + "'\n"; 929 } 930 if (tag == "14") { 931 return indent + "TeletexString '" + hextoutf8(_getV(hex, idx)) + "'\n"; 932 } 933 if (tag == "16") { 934 return indent + "IA5String '" + hextoutf8(_getV(hex, idx)) + "'\n"; 935 } 936 if (tag == "17") { 937 return indent + "UTCTime " + hextoutf8(_getV(hex, idx)) + "\n"; 938 } 939 if (tag == "18") { 940 return indent + "GeneralizedTime " + hextoutf8(_getV(hex, idx)) + "\n"; 941 } 942 if (tag == "1a") { 943 return indent + "VisualString '" + hextoutf8(_getV(hex, idx)) + "'\n"; 944 } 945 if (tag == "1e") { 946 return indent + "BMPString '" + ucs2hextoutf8(_getV(hex, idx)) + "'\n"; 947 } 948 if (tag == "30") { 949 if (hex.substr(idx, 4) == "3000") { 950 return indent + "SEQUENCE {}\n"; 951 } 952 953 var s = indent + "SEQUENCE\n"; 954 var aIdx = _getChildIdx(hex, idx); 955 956 var flagsTemp = flags; 957 958 if ((aIdx.length == 2 || aIdx.length == 3) && 959 hex.substr(aIdx[0], 2) == "06" && 960 hex.substr(aIdx[aIdx.length - 1], 2) == "04") { // supposed X.509v3 extension 961 var oidName = _ASN1HEX.oidname(_getV(hex, aIdx[0])); 962 var flagsClone = JSON.parse(JSON.stringify(flags)); 963 flagsClone.x509ExtName = oidName; 964 flagsTemp = flagsClone; 965 } 966 967 for (var i = 0; i < aIdx.length; i++) { 968 s = s + _dump(hex, flagsTemp, aIdx[i], indent + " "); 969 } 970 return s; 971 } 972 if (tag == "31") { 973 var s = indent + "SET\n"; 974 var aIdx = _getChildIdx(hex, idx); 975 for (var i = 0; i < aIdx.length; i++) { 976 s = s + _dump(hex, flags, aIdx[i], indent + " "); 977 } 978 return s; 979 } 980 var tag = parseInt(tag, 16); 981 if ((tag & 128) != 0) { // context specific 982 var tagNumber = tag & 31; 983 if ((tag & 32) != 0) { // structured tag 984 var s = indent + "[" + tagNumber + "]\n"; 985 var aIdx = _getChildIdx(hex, idx); 986 for (var i = 0; i < aIdx.length; i++) { 987 s = s + _dump(hex, flags, aIdx[i], indent + " "); 988 } 989 return s; 990 } else { // primitive tag 991 var v = _getV(hex, idx); 992 if (ASN1HEX.isASN1HEX(v)) { 993 var s = indent + "[" + tagNumber + "]\n"; 994 s = s + _dump(v, flags, 0, indent + " "); 995 return s; 996 } else if (v.substr(0, 8) == "68747470") { // http 997 v = hextoutf8(v); 998 } else if (flags.x509ExtName === "subjectAltName" && 999 tagNumber == 2) { 1000 v = hextoutf8(v); 1001 } 1002 // else if (ASN1HEX.isASN1HEX(v)) 1003 1004 var s = indent + "[" + tagNumber + "] " + v + "\n"; 1005 return s; 1006 } 1007 } 1008 return indent + "UNKNOWN(" + tag + ") " + 1009 _getV(hex, idx) + "\n"; 1010 }; 1011 1012 /** 1013 * parse ASN.1 DER hexadecimal string<br/> 1014 * @name parse 1015 * @memberOf ASN1HEX 1016 * @function 1017 * @param {String} h hexadecimal string of ASN1. DER 1018 * @return {Object} associative array of ASN.1 parsed result 1019 * @since jsrsasign 10.5.3 asn1hex 1.1.x 1020 * @see KJUR.asn1.ASN1Util.newOjbect 1021 * 1022 * @description 1023 * This method parses ASN.1 DER hexadecimal string. 1024 * Its result can be applied to {@link KJUR.asn1.ASN1Util.newOjbect}. 1025 * 1026 * @example 1027 * ASN1HEX.parse("31193017...") → // RDN 1028 * {set: [{seq: [{oid: "localityName"}, {utf8str: {str: "Test"}}] }]} 1029 */ 1030 ASN1HEX.parse = function(h) { 1031 var _ASN1HEX = ASN1HEX, 1032 _parse = _ASN1HEX.parse, 1033 _isASN1HEX = _ASN1HEX.isASN1HEX, 1034 _getV = _ASN1HEX.getV, 1035 _getTLV = _ASN1HEX.getTLV, 1036 _getChildIdx = _ASN1HEX.getChildIdx, 1037 _KJUR_asn1 = KJUR.asn1, 1038 _oidHexToInt = _KJUR_asn1.ASN1Util.oidHexToInt, 1039 _oid2name = _KJUR_asn1.x509.OID.oid2name, 1040 _hextoutf8 = hextoutf8, 1041 _ucs2hextoutf8 = ucs2hextoutf8, 1042 _iso88591hextoutf8 = iso88591hextoutf8; 1043 1044 var tagName = { 1045 "0c": "utf8str", "12": "numstr", "13": "prnstr", 1046 "14": "telstr", "16": "ia5str", "17": "utctime", 1047 "18": "gentime", "1a": "visstr", "1e": "bmpstr", 1048 "30": "seq", "31": "set" 1049 }; 1050 1051 var _parseChild = function(h) { 1052 var result = []; 1053 var aIdx = _getChildIdx(h, 0); 1054 for (var i = 0; i < aIdx.length; i++) { 1055 var idx = aIdx[i]; 1056 var hTLV = _getTLV(h, idx); 1057 var pItem = _parse(hTLV); 1058 result.push(pItem); 1059 } 1060 return result; 1061 }; 1062 1063 var tag = h.substr(0, 2); 1064 var result = {}; 1065 var hV = _getV(h, 0); 1066 if (tag == "01") { 1067 if (h == "0101ff") return {bool: true}; 1068 return {bool: false}; 1069 } else if (tag == "02") { 1070 return {"int": {hex: hV}}; 1071 } else if (tag == "03") { 1072 try { 1073 if (hV.substr(0, 2) != "00") throw "not encap"; 1074 var hV1 = hV.substr(2); 1075 if (! _isASN1HEX(hV1)) throw "not encap"; 1076 return {bitstr: {obj: _parse(hV1)}}; 1077 } catch(ex) { 1078 var bV = null; 1079 if (hV.length <= 10) bV = bitstrtobinstr(hV); 1080 if (bV == null) { 1081 return {bitstr: {hex: hV}}; 1082 } else { 1083 return {bitstr: {bin: bV}}; 1084 } 1085 } 1086 } else if (tag == "04") { 1087 try { 1088 if (! _isASN1HEX(hV)) throw "not encap"; 1089 return {octstr: {obj: _parse(hV)}}; 1090 } catch(ex) { 1091 return {octstr: {hex: hV}}; 1092 } 1093 } else if (tag == "05") { 1094 return {"null": ''}; 1095 } else if (tag == "06") { 1096 var oidDot = _oidHexToInt(hV); 1097 var oidName = _oid2name(oidDot); 1098 if (oidName == "") { 1099 return {oid: oidDot}; 1100 } else { 1101 return {oid: oidName}; 1102 } 1103 } else if (tag == "0a") { 1104 if (hV.length > 4) { 1105 return {"enum": {hex: hV}}; 1106 } else { 1107 return {"enum": parseInt(hV, 16)}; 1108 } 1109 } else if (tag == "30" || tag == "31") { 1110 result[tagName[tag]] = _parseChild(h); 1111 return result; 1112 } else if (tag == "14") { // TeletexString 1113 var s = _iso88591hextoutf8(hV); 1114 result[tagName[tag]] = {str: s}; 1115 return result; 1116 } else if (tag == "1e") { // BMPString 1117 var s = _ucs2hextoutf8(hV); 1118 result[tagName[tag]] = {str: s}; 1119 return result; 1120 } else if (":0c:12:13:16:17:18:1a:".indexOf(tag) != -1) { // Other Strings types 1121 var s = _hextoutf8(hV); 1122 result[tagName[tag]] = {str: s}; 1123 return result; 1124 } else if (tag.match(/^8[0-9]$/)) { 1125 var s = _hextoutf8(hV); 1126 if (s == null | s == "") { 1127 return {"tag": {"tag": tag, explicit: false, hex: hV}}; 1128 } else if (s.match(/[\x00-\x1F\x7F-\x9F]/) != null || 1129 s.match(/[\u0000-\u001F\u0080–\u009F]/) != null) { 1130 return {"tag": {"tag": tag, explicit: false, hex: hV}}; 1131 } else { 1132 return {"tag": {"tag": tag, explicit: false, str: s}}; 1133 } 1134 } else if (tag.match(/^a[0-9]$/)) { 1135 try { 1136 if (! _isASN1HEX(hV)) throw new Error("not encap"); 1137 return {"tag": {"tag": tag, 1138 explicit: true, 1139 obj: _parse(hV)}}; 1140 } catch(ex) { 1141 return {"tag": {"tag": tag, explicit: true, hex: hV}}; 1142 } 1143 } else { 1144 var d = new KJUR.asn1.ASN1Object(); 1145 d.hV = hV; 1146 var hL = d.getLengthHexFromValue(); 1147 return {"asn1": {"tlv": tag + hL + hV}}; 1148 } 1149 }; 1150 1151 /** 1152 * check if a hexadecimal tag is a specified ASN.1 context specific tag 1153 * @name isContextTag 1154 * @memberOf ASN1HEX 1155 * @function 1156 * @param {hTag} hex string of a hexadecimal ASN.1 tag consists by two characters (e.x. "a0") 1157 * @param {sTag} context specific tag in string represention (OPTION) (e.x. "[0]") 1158 * @return {Boolean} true if hTag is a ASN.1 context specific tag specified by sTag value. 1159 * @since jsrsasign 8.0.21 asn1hex 1.2.2 1160 * @description 1161 * This method checks if a hexadecimal tag is a specified ASN.1 context specific tag. 1162 * Structured and non-structured type of tag have the same string representation 1163 * of context specific tag. For example tag "a0" and "80" have the same string 1164 * representation "[0]". 1165 * The sTag has a range from from "[0]" to "[31]". 1166 * @example 1167 * ASN1HEX.isContextTag('a0', '[0]') → true // structured 1168 * ASN1HEX.isContextTag('a1', '[1]') → true // structured 1169 * ASN1HEX.isContextTag('a2', '[2]') → true // structured 1170 * ASN1HEX.isContextTag('80', '[0]') → true // non structured 1171 * ASN1HEX.isContextTag('81', '[1]') → true // non structured 1172 * ASN1HEX.isContextTag('82', '[2]') → true // non structured 1173 * ASN1HEX.isContextTag('a0', '[3]') → false 1174 * ASN1HEX.isContextTag('80', '[15]') → false 1175 * 1176 * ASN.1 tag bits 1177 * 12345679 1178 * ++ tag class(universal:00, context specific:10) 1179 * + structured:1, primitive:0 1180 * +++++ tag number (0 - 31) 1181 */ 1182 ASN1HEX.isContextTag = function(hTag, sTag) { 1183 hTag = hTag.toLowerCase(); 1184 var ihtag, istag; 1185 1186 try { 1187 ihtag = parseInt(hTag, 16); 1188 } catch (ex) { 1189 return -1; 1190 } 1191 1192 if (sTag === undefined) { 1193 if ((ihtag & 192) == 128) { 1194 return true; 1195 } else { 1196 return false; 1197 } 1198 } 1199 1200 try { 1201 var result = sTag.match(/^\[[0-9]+\]$/); 1202 if (result == null) return false; 1203 istag = parseInt(sTag.substr(1,sTag.length - 1), 10); 1204 if (istag > 31) return false; 1205 if (((ihtag & 192) == 128) && // ihtag & b11000000 == b10000000 1206 ((ihtag & 31) == istag)) { // ihtag & b00011111 == istag (0-31) 1207 return true; 1208 } 1209 return false; 1210 } catch (ex) { 1211 return false; 1212 } 1213 }; 1214 1215 /** 1216 * simple ASN.1 DER hexadecimal string checker<br/> 1217 * @name isASN1HEX 1218 * @memberOf ASN1HEX 1219 * @function 1220 * @param {String} hex string to check whether it is hexadecmal string for ASN.1 DER or not 1221 * @return {Boolean} true if it is hexadecimal string of ASN.1 data otherwise false 1222 * @since jsrsasign 4.8.3 asn1hex 1.1.6 1223 * @description 1224 * This method checks wheather the argument 'hex' is a hexadecimal string of 1225 * ASN.1 data or not. 1226 * @example 1227 * ASN1HEX.isASN1HEX('0203012345') → true // PROPER ASN.1 INTEGER 1228 * ASN1HEX.isASN1HEX('0203012345ff') → false // TOO LONG VALUE 1229 * ASN1HEX.isASN1HEX('02030123') → false // TOO SHORT VALUE 1230 * ASN1HEX.isASN1HEX('fa3bcd') → false // WRONG FOR ASN.1 1231 */ 1232 ASN1HEX.isASN1HEX = function(hex) { 1233 var _ASN1HEX = ASN1HEX; 1234 if (hex.length % 2 == 1) return false; 1235 1236 var intL = _ASN1HEX.getVblen(hex, 0); 1237 var hT = hex.substr(0, 2); 1238 var hL = _ASN1HEX.getL(hex, 0); 1239 var hVLength = hex.length - hT.length - hL.length; 1240 if (hVLength == intL * 2) return true; 1241 1242 return false; 1243 }; 1244 1245 /** 1246 * strict ASN.1 DER hexadecimal string checker 1247 * @name checkStrictDER 1248 * @memberOf ASN1HEX 1249 * @function 1250 * @param {String} hex string to check whether it is hexadecmal string for ASN.1 DER or not 1251 * @return unspecified 1252 * @since jsrsasign 8.0.19 asn1hex 1.2.1 1253 * @throws Error when malformed ASN.1 DER hexadecimal string 1254 * @description 1255 * This method checks wheather the argument 'hex' is a hexadecimal string of 1256 * ASN.1 data or not. If the argument is not DER string, this 1257 * raise an exception. 1258 * @example 1259 * ASN1HEX.checkStrictDER('0203012345') → NO EXCEPTION FOR PROPER ASN.1 INTEGER 1260 * ASN1HEX.checkStrictDER('0203012345ff') → RAISE EXCEPTION FOR TOO LONG VALUE 1261 * ASN1HEX.checkStrictDER('02030123') → false RAISE EXCEPITON FOR TOO SHORT VALUE 1262 * ASN1HEX.checkStrictDER('fa3bcd') → false RAISE EXCEPTION FOR WRONG ASN.1 1263 */ 1264 ASN1HEX.checkStrictDER = function(h, idx, maxHexLen, maxByteLen, maxLbyteLen) { 1265 var _ASN1HEX = ASN1HEX; 1266 1267 if (maxHexLen === undefined) { 1268 // 1. hex string check 1269 if (typeof h != "string") throw new Error("not hex string"); 1270 h = h.toLowerCase(); 1271 if (! KJUR.lang.String.isHex(h)) throw new Error("not hex string"); 1272 1273 // 2. set max if needed 1274 // max length of hexadecimal string 1275 maxHexLen = h.length; 1276 // max length of octets 1277 maxByteLen = h.length / 2; 1278 // max length of L octets of TLV 1279 if (maxByteLen < 0x80) { 1280 maxLbyteLen = 1; 1281 } else { 1282 maxLbyteLen = Math.ceil(maxByteLen.toString(16)) + 1; 1283 } 1284 } 1285 //console.log(maxHexLen + ":" + maxByteLen + ":" + maxLbyteLen); 1286 1287 // 3. check if L(length) string not exceeds maxLbyteLen 1288 var hL = _ASN1HEX.getL(h, idx); 1289 if (hL.length > maxLbyteLen * 2) 1290 throw new Error("L of TLV too long: idx=" + idx); 1291 1292 // 4. check if V(value) octet length (i.e. L(length) value) 1293 // not exceeds maxByteLen 1294 var vblen = _ASN1HEX.getVblen(h, idx); 1295 if (vblen > maxByteLen) 1296 throw new Error("value of L too long than hex: idx=" + idx); 1297 1298 // 5. check V string length and L's value are the same 1299 var hTLV = _ASN1HEX.getTLV(h, idx); 1300 var hVLength = 1301 hTLV.length - 2 - _ASN1HEX.getL(h, idx).length; 1302 if (hVLength !== (vblen * 2)) 1303 throw new Error("V string length and L's value not the same:" + 1304 hVLength + "/" + (vblen * 2)); 1305 1306 // 6. check appending garbled string 1307 if (idx === 0) { 1308 if (h.length != hTLV.length) 1309 throw new Error("total length and TLV length unmatch:" + 1310 h.length + "!=" + hTLV.length); 1311 } 1312 1313 // 7. check if there isn't prepending zeros in DER INTEGER value 1314 var hT = h.substr(idx, 2); 1315 if (hT === '02') { 1316 var vidx = _ASN1HEX.getVidx(h, idx); 1317 // check if DER INTEGER VALUE have least leading zeros 1318 // for two's complement 1319 // GOOD - 3fabde... 008fad... 1320 // BAD - 000012... 007fad... 1321 if (h.substr(vidx, 2) == "00" && h.charCodeAt(vidx + 2) < 56) // '8'=56 1322 throw new Error("not least zeros for DER INTEGER"); 1323 } 1324 1325 // 8. check if all of elements in a structured item are conformed to 1326 // strict DER encoding rules. 1327 if (parseInt(hT, 16) & 32) { // structured tag? 1328 var intL = _ASN1HEX.getVblen(h, idx); 1329 var sum = 0; 1330 var aIdx = _ASN1HEX.getChildIdx(h, idx); 1331 for (var i = 0; i < aIdx.length; i++) { 1332 var tlv = _ASN1HEX.getTLV(h, aIdx[i]); 1333 sum += tlv.length; 1334 _ASN1HEX.checkStrictDER(h, aIdx[i], 1335 maxHexLen, maxByteLen, maxLbyteLen); 1336 } 1337 if ((intL * 2) != sum) 1338 throw new Error("sum of children's TLV length and L unmatch: " + 1339 (intL * 2) + "!=" + sum); 1340 } 1341 }; 1342 1343 /** 1344 * get hexacedimal string from PEM format data<br/> 1345 * @name oidname 1346 * @memberOf ASN1HEX 1347 * @function 1348 * @param {String} oidDotOrHex number dot notation(i.e. 1.2.3) or hexadecimal string for OID 1349 * @return {String} name for OID 1350 * @since jsrsasign 7.2.0 asn1hex 1.1.11 1351 * @description 1352 * This static method gets a OID name for 1353 * a specified string of number dot notation (i.e. 1.2.3) or 1354 * hexadecimal string. 1355 * @example 1356 * ASN1HEX.oidname("2.5.29.37") → extKeyUsage 1357 * ASN1HEX.oidname("551d25") → extKeyUsage 1358 * ASN1HEX.oidname("0.1.2.3") → 0.1.2.3 // unknown 1359 */ 1360 ASN1HEX.oidname = function(oidDotOrHex) { 1361 var _KJUR_asn1 = KJUR.asn1; 1362 if (KJUR.lang.String.isHex(oidDotOrHex)) 1363 oidDotOrHex = _KJUR_asn1.ASN1Util.oidHexToInt(oidDotOrHex); 1364 var name = _KJUR_asn1.x509.OID.oid2name(oidDotOrHex); 1365 if (name === "") name = oidDotOrHex; 1366 return name; 1367 }; 1368 1369