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