| // ASN.1 JavaScript decoder | 
| // Copyright (c) 2008-2014 Lapo Luchini <lapo@lapo.it> | 
| // Permission to use, copy, modify, and/or distribute this software for any | 
| // purpose with or without fee is hereby granted, provided that the above | 
| // copyright notice and this permission notice appear in all copies. | 
| // | 
| // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | 
| // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | 
| // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | 
| // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | 
| // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | 
| // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | 
| // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | 
| /*jshint browser: true, strict: true, immed: true, latedef: true, undef: true, regexdash: false */ | 
| /*global oids */ | 
| import { Int10 } from "./int10"; | 
| var ellipsis = "\u2026"; | 
| var reTimeS = /^(\d\d)(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])([01]\d|2[0-3])(?:([0-5]\d)(?:([0-5]\d)(?:[.,](\d{1,3}))?)?)?(Z|[-+](?:[0]\d|1[0-2])([0-5]\d)?)?$/; | 
| var reTimeL = /^(\d\d\d\d)(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])([01]\d|2[0-3])(?:([0-5]\d)(?:([0-5]\d)(?:[.,](\d{1,3}))?)?)?(Z|[-+](?:[0]\d|1[0-2])([0-5]\d)?)?$/; | 
| function stringCut(str, len) { | 
|     if (str.length > len) { | 
|         str = str.substring(0, len) + ellipsis; | 
|     } | 
|     return str; | 
| } | 
| var Stream = /** @class */ (function () { | 
|     function Stream(enc, pos) { | 
|         this.hexDigits = "0123456789ABCDEF"; | 
|         if (enc instanceof Stream) { | 
|             this.enc = enc.enc; | 
|             this.pos = enc.pos; | 
|         } | 
|         else { | 
|             // enc should be an array or a binary string | 
|             this.enc = enc; | 
|             this.pos = pos; | 
|         } | 
|     } | 
|     Stream.prototype.get = function (pos) { | 
|         if (pos === undefined) { | 
|             pos = this.pos++; | 
|         } | 
|         if (pos >= this.enc.length) { | 
|             throw new Error("Requesting byte offset ".concat(pos, " on a stream of length ").concat(this.enc.length)); | 
|         } | 
|         return ("string" === typeof this.enc) ? this.enc.charCodeAt(pos) : this.enc[pos]; | 
|     }; | 
|     Stream.prototype.hexByte = function (b) { | 
|         return this.hexDigits.charAt((b >> 4) & 0xF) + this.hexDigits.charAt(b & 0xF); | 
|     }; | 
|     Stream.prototype.hexDump = function (start, end, raw) { | 
|         var s = ""; | 
|         for (var i = start; i < end; ++i) { | 
|             s += this.hexByte(this.get(i)); | 
|             if (raw !== true) { | 
|                 switch (i & 0xF) { | 
|                     case 0x7: | 
|                         s += "  "; | 
|                         break; | 
|                     case 0xF: | 
|                         s += "\n"; | 
|                         break; | 
|                     default: | 
|                         s += " "; | 
|                 } | 
|             } | 
|         } | 
|         return s; | 
|     }; | 
|     Stream.prototype.isASCII = function (start, end) { | 
|         for (var i = start; i < end; ++i) { | 
|             var c = this.get(i); | 
|             if (c < 32 || c > 176) { | 
|                 return false; | 
|             } | 
|         } | 
|         return true; | 
|     }; | 
|     Stream.prototype.parseStringISO = function (start, end) { | 
|         var s = ""; | 
|         for (var i = start; i < end; ++i) { | 
|             s += String.fromCharCode(this.get(i)); | 
|         } | 
|         return s; | 
|     }; | 
|     Stream.prototype.parseStringUTF = function (start, end) { | 
|         var s = ""; | 
|         for (var i = start; i < end;) { | 
|             var c = this.get(i++); | 
|             if (c < 128) { | 
|                 s += String.fromCharCode(c); | 
|             } | 
|             else if ((c > 191) && (c < 224)) { | 
|                 s += String.fromCharCode(((c & 0x1F) << 6) | (this.get(i++) & 0x3F)); | 
|             } | 
|             else { | 
|                 s += String.fromCharCode(((c & 0x0F) << 12) | ((this.get(i++) & 0x3F) << 6) | (this.get(i++) & 0x3F)); | 
|             } | 
|         } | 
|         return s; | 
|     }; | 
|     Stream.prototype.parseStringBMP = function (start, end) { | 
|         var str = ""; | 
|         var hi; | 
|         var lo; | 
|         for (var i = start; i < end;) { | 
|             hi = this.get(i++); | 
|             lo = this.get(i++); | 
|             str += String.fromCharCode((hi << 8) | lo); | 
|         } | 
|         return str; | 
|     }; | 
|     Stream.prototype.parseTime = function (start, end, shortYear) { | 
|         var s = this.parseStringISO(start, end); | 
|         var m = (shortYear ? reTimeS : reTimeL).exec(s); | 
|         if (!m) { | 
|             return "Unrecognized time: " + s; | 
|         } | 
|         if (shortYear) { | 
|             // to avoid querying the timer, use the fixed range [1970, 2069] | 
|             // it will conform with ITU X.400 [-10, +40] sliding window until 2030 | 
|             m[1] = +m[1]; | 
|             m[1] += (+m[1] < 70) ? 2000 : 1900; | 
|         } | 
|         s = m[1] + "-" + m[2] + "-" + m[3] + " " + m[4]; | 
|         if (m[5]) { | 
|             s += ":" + m[5]; | 
|             if (m[6]) { | 
|                 s += ":" + m[6]; | 
|                 if (m[7]) { | 
|                     s += "." + m[7]; | 
|                 } | 
|             } | 
|         } | 
|         if (m[8]) { | 
|             s += " UTC"; | 
|             if (m[8] != "Z") { | 
|                 s += m[8]; | 
|                 if (m[9]) { | 
|                     s += ":" + m[9]; | 
|                 } | 
|             } | 
|         } | 
|         return s; | 
|     }; | 
|     Stream.prototype.parseInteger = function (start, end) { | 
|         var v = this.get(start); | 
|         var neg = (v > 127); | 
|         var pad = neg ? 255 : 0; | 
|         var len; | 
|         var s = ""; | 
|         // skip unuseful bits (not allowed in DER) | 
|         while (v == pad && ++start < end) { | 
|             v = this.get(start); | 
|         } | 
|         len = end - start; | 
|         if (len === 0) { | 
|             return neg ? -1 : 0; | 
|         } | 
|         // show bit length of huge integers | 
|         if (len > 4) { | 
|             s = v; | 
|             len <<= 3; | 
|             while (((+s ^ pad) & 0x80) == 0) { | 
|                 s = +s << 1; | 
|                 --len; | 
|             } | 
|             s = "(" + len + " bit)\n"; | 
|         } | 
|         // decode the integer | 
|         if (neg) { | 
|             v = v - 256; | 
|         } | 
|         var n = new Int10(v); | 
|         for (var i = start + 1; i < end; ++i) { | 
|             n.mulAdd(256, this.get(i)); | 
|         } | 
|         return s + n.toString(); | 
|     }; | 
|     Stream.prototype.parseBitString = function (start, end, maxLength) { | 
|         var unusedBit = this.get(start); | 
|         var lenBit = ((end - start - 1) << 3) - unusedBit; | 
|         var intro = "(" + lenBit + " bit)\n"; | 
|         var s = ""; | 
|         for (var i = start + 1; i < end; ++i) { | 
|             var b = this.get(i); | 
|             var skip = (i == end - 1) ? unusedBit : 0; | 
|             for (var j = 7; j >= skip; --j) { | 
|                 s += (b >> j) & 1 ? "1" : "0"; | 
|             } | 
|             if (s.length > maxLength) { | 
|                 return intro + stringCut(s, maxLength); | 
|             } | 
|         } | 
|         return intro + s; | 
|     }; | 
|     Stream.prototype.parseOctetString = function (start, end, maxLength) { | 
|         if (this.isASCII(start, end)) { | 
|             return stringCut(this.parseStringISO(start, end), maxLength); | 
|         } | 
|         var len = end - start; | 
|         var s = "(" + len + " byte)\n"; | 
|         maxLength /= 2; // we work in bytes | 
|         if (len > maxLength) { | 
|             end = start + maxLength; | 
|         } | 
|         for (var i = start; i < end; ++i) { | 
|             s += this.hexByte(this.get(i)); | 
|         } | 
|         if (len > maxLength) { | 
|             s += ellipsis; | 
|         } | 
|         return s; | 
|     }; | 
|     Stream.prototype.parseOID = function (start, end, maxLength) { | 
|         var s = ""; | 
|         var n = new Int10(); | 
|         var bits = 0; | 
|         for (var i = start; i < end; ++i) { | 
|             var v = this.get(i); | 
|             n.mulAdd(128, v & 0x7F); | 
|             bits += 7; | 
|             if (!(v & 0x80)) { // finished | 
|                 if (s === "") { | 
|                     n = n.simplify(); | 
|                     if (n instanceof Int10) { | 
|                         n.sub(80); | 
|                         s = "2." + n.toString(); | 
|                     } | 
|                     else { | 
|                         var m = n < 80 ? n < 40 ? 0 : 1 : 2; | 
|                         s = m + "." + (n - m * 40); | 
|                     } | 
|                 } | 
|                 else { | 
|                     s += "." + n.toString(); | 
|                 } | 
|                 if (s.length > maxLength) { | 
|                     return stringCut(s, maxLength); | 
|                 } | 
|                 n = new Int10(); | 
|                 bits = 0; | 
|             } | 
|         } | 
|         if (bits > 0) { | 
|             s += ".incomplete"; | 
|         } | 
|         return s; | 
|     }; | 
|     return Stream; | 
| }()); | 
| export { Stream }; | 
| var ASN1 = /** @class */ (function () { | 
|     function ASN1(stream, header, length, tag, sub) { | 
|         if (!(tag instanceof ASN1Tag)) { | 
|             throw new Error("Invalid tag value."); | 
|         } | 
|         this.stream = stream; | 
|         this.header = header; | 
|         this.length = length; | 
|         this.tag = tag; | 
|         this.sub = sub; | 
|     } | 
|     ASN1.prototype.typeName = function () { | 
|         switch (this.tag.tagClass) { | 
|             case 0: // universal | 
|                 switch (this.tag.tagNumber) { | 
|                     case 0x00: | 
|                         return "EOC"; | 
|                     case 0x01: | 
|                         return "BOOLEAN"; | 
|                     case 0x02: | 
|                         return "INTEGER"; | 
|                     case 0x03: | 
|                         return "BIT_STRING"; | 
|                     case 0x04: | 
|                         return "OCTET_STRING"; | 
|                     case 0x05: | 
|                         return "NULL"; | 
|                     case 0x06: | 
|                         return "OBJECT_IDENTIFIER"; | 
|                     case 0x07: | 
|                         return "ObjectDescriptor"; | 
|                     case 0x08: | 
|                         return "EXTERNAL"; | 
|                     case 0x09: | 
|                         return "REAL"; | 
|                     case 0x0A: | 
|                         return "ENUMERATED"; | 
|                     case 0x0B: | 
|                         return "EMBEDDED_PDV"; | 
|                     case 0x0C: | 
|                         return "UTF8String"; | 
|                     case 0x10: | 
|                         return "SEQUENCE"; | 
|                     case 0x11: | 
|                         return "SET"; | 
|                     case 0x12: | 
|                         return "NumericString"; | 
|                     case 0x13: | 
|                         return "PrintableString"; // ASCII subset | 
|                     case 0x14: | 
|                         return "TeletexString"; // aka T61String | 
|                     case 0x15: | 
|                         return "VideotexString"; | 
|                     case 0x16: | 
|                         return "IA5String"; // ASCII | 
|                     case 0x17: | 
|                         return "UTCTime"; | 
|                     case 0x18: | 
|                         return "GeneralizedTime"; | 
|                     case 0x19: | 
|                         return "GraphicString"; | 
|                     case 0x1A: | 
|                         return "VisibleString"; // ASCII subset | 
|                     case 0x1B: | 
|                         return "GeneralString"; | 
|                     case 0x1C: | 
|                         return "UniversalString"; | 
|                     case 0x1E: | 
|                         return "BMPString"; | 
|                 } | 
|                 return "Universal_" + this.tag.tagNumber.toString(); | 
|             case 1: | 
|                 return "Application_" + this.tag.tagNumber.toString(); | 
|             case 2: | 
|                 return "[" + this.tag.tagNumber.toString() + "]"; // Context | 
|             case 3: | 
|                 return "Private_" + this.tag.tagNumber.toString(); | 
|         } | 
|     }; | 
|     ASN1.prototype.content = function (maxLength) { | 
|         if (this.tag === undefined) { | 
|             return null; | 
|         } | 
|         if (maxLength === undefined) { | 
|             maxLength = Infinity; | 
|         } | 
|         var content = this.posContent(); | 
|         var len = Math.abs(this.length); | 
|         if (!this.tag.isUniversal()) { | 
|             if (this.sub !== null) { | 
|                 return "(" + this.sub.length + " elem)"; | 
|             } | 
|             return this.stream.parseOctetString(content, content + len, maxLength); | 
|         } | 
|         switch (this.tag.tagNumber) { | 
|             case 0x01: // BOOLEAN | 
|                 return (this.stream.get(content) === 0) ? "false" : "true"; | 
|             case 0x02: // INTEGER | 
|                 return this.stream.parseInteger(content, content + len); | 
|             case 0x03: // BIT_STRING | 
|                 return this.sub ? "(" + this.sub.length + " elem)" : | 
|                     this.stream.parseBitString(content, content + len, maxLength); | 
|             case 0x04: // OCTET_STRING | 
|                 return this.sub ? "(" + this.sub.length + " elem)" : | 
|                     this.stream.parseOctetString(content, content + len, maxLength); | 
|             // case 0x05: // NULL | 
|             case 0x06: // OBJECT_IDENTIFIER | 
|                 return this.stream.parseOID(content, content + len, maxLength); | 
|             // case 0x07: // ObjectDescriptor | 
|             // case 0x08: // EXTERNAL | 
|             // case 0x09: // REAL | 
|             // case 0x0A: // ENUMERATED | 
|             // case 0x0B: // EMBEDDED_PDV | 
|             case 0x10: // SEQUENCE | 
|             case 0x11: // SET | 
|                 if (this.sub !== null) { | 
|                     return "(" + this.sub.length + " elem)"; | 
|                 } | 
|                 else { | 
|                     return "(no elem)"; | 
|                 } | 
|             case 0x0C: // UTF8String | 
|                 return stringCut(this.stream.parseStringUTF(content, content + len), maxLength); | 
|             case 0x12: // NumericString | 
|             case 0x13: // PrintableString | 
|             case 0x14: // TeletexString | 
|             case 0x15: // VideotexString | 
|             case 0x16: // IA5String | 
|             // case 0x19: // GraphicString | 
|             case 0x1A: // VisibleString | 
|                 // case 0x1B: // GeneralString | 
|                 // case 0x1C: // UniversalString | 
|                 return stringCut(this.stream.parseStringISO(content, content + len), maxLength); | 
|             case 0x1E: // BMPString | 
|                 return stringCut(this.stream.parseStringBMP(content, content + len), maxLength); | 
|             case 0x17: // UTCTime | 
|             case 0x18: // GeneralizedTime | 
|                 return this.stream.parseTime(content, content + len, (this.tag.tagNumber == 0x17)); | 
|         } | 
|         return null; | 
|     }; | 
|     ASN1.prototype.toString = function () { | 
|         return this.typeName() + "@" + this.stream.pos + "[header:" + this.header + ",length:" + this.length + ",sub:" + ((this.sub === null) ? "null" : this.sub.length) + "]"; | 
|     }; | 
|     ASN1.prototype.toPrettyString = function (indent) { | 
|         if (indent === undefined) { | 
|             indent = ""; | 
|         } | 
|         var s = indent + this.typeName() + " @" + this.stream.pos; | 
|         if (this.length >= 0) { | 
|             s += "+"; | 
|         } | 
|         s += this.length; | 
|         if (this.tag.tagConstructed) { | 
|             s += " (constructed)"; | 
|         } | 
|         else if ((this.tag.isUniversal() && ((this.tag.tagNumber == 0x03) || (this.tag.tagNumber == 0x04))) && (this.sub !== null)) { | 
|             s += " (encapsulates)"; | 
|         } | 
|         s += "\n"; | 
|         if (this.sub !== null) { | 
|             indent += "  "; | 
|             for (var i = 0, max = this.sub.length; i < max; ++i) { | 
|                 s += this.sub[i].toPrettyString(indent); | 
|             } | 
|         } | 
|         return s; | 
|     }; | 
|     ASN1.prototype.posStart = function () { | 
|         return this.stream.pos; | 
|     }; | 
|     ASN1.prototype.posContent = function () { | 
|         return this.stream.pos + this.header; | 
|     }; | 
|     ASN1.prototype.posEnd = function () { | 
|         return this.stream.pos + this.header + Math.abs(this.length); | 
|     }; | 
|     ASN1.prototype.toHexString = function () { | 
|         return this.stream.hexDump(this.posStart(), this.posEnd(), true); | 
|     }; | 
|     ASN1.decodeLength = function (stream) { | 
|         var buf = stream.get(); | 
|         var len = buf & 0x7F; | 
|         if (len == buf) { | 
|             return len; | 
|         } | 
|         // no reason to use Int10, as it would be a huge buffer anyways | 
|         if (len > 6) { | 
|             throw new Error("Length over 48 bits not supported at position " + (stream.pos - 1)); | 
|         } | 
|         if (len === 0) { | 
|             return null; | 
|         } // undefined | 
|         buf = 0; | 
|         for (var i = 0; i < len; ++i) { | 
|             buf = (buf * 256) + stream.get(); | 
|         } | 
|         return buf; | 
|     }; | 
|     /** | 
|      * Retrieve the hexadecimal value (as a string) of the current ASN.1 element | 
|      * @returns {string} | 
|      * @public | 
|      */ | 
|     ASN1.prototype.getHexStringValue = function () { | 
|         var hexString = this.toHexString(); | 
|         var offset = this.header * 2; | 
|         var length = this.length * 2; | 
|         return hexString.substr(offset, length); | 
|     }; | 
|     ASN1.decode = function (str) { | 
|         var stream; | 
|         if (!(str instanceof Stream)) { | 
|             stream = new Stream(str, 0); | 
|         } | 
|         else { | 
|             stream = str; | 
|         } | 
|         var streamStart = new Stream(stream); | 
|         var tag = new ASN1Tag(stream); | 
|         var len = ASN1.decodeLength(stream); | 
|         var start = stream.pos; | 
|         var header = start - streamStart.pos; | 
|         var sub = null; | 
|         var getSub = function () { | 
|             var ret = []; | 
|             if (len !== null) { | 
|                 // definite length | 
|                 var end = start + len; | 
|                 while (stream.pos < end) { | 
|                     ret[ret.length] = ASN1.decode(stream); | 
|                 } | 
|                 if (stream.pos != end) { | 
|                     throw new Error("Content size is not correct for container starting at offset " + start); | 
|                 } | 
|             } | 
|             else { | 
|                 // undefined length | 
|                 try { | 
|                     for (;;) { | 
|                         var s = ASN1.decode(stream); | 
|                         if (s.tag.isEOC()) { | 
|                             break; | 
|                         } | 
|                         ret[ret.length] = s; | 
|                     } | 
|                     len = start - stream.pos; // undefined lengths are represented as negative values | 
|                 } | 
|                 catch (e) { | 
|                     throw new Error("Exception while decoding undefined length content: " + e); | 
|                 } | 
|             } | 
|             return ret; | 
|         }; | 
|         if (tag.tagConstructed) { | 
|             // must have valid content | 
|             sub = getSub(); | 
|         } | 
|         else if (tag.isUniversal() && ((tag.tagNumber == 0x03) || (tag.tagNumber == 0x04))) { | 
|             // sometimes BitString and OctetString are used to encapsulate ASN.1 | 
|             try { | 
|                 if (tag.tagNumber == 0x03) { | 
|                     if (stream.get() != 0) { | 
|                         throw new Error("BIT STRINGs with unused bits cannot encapsulate."); | 
|                     } | 
|                 } | 
|                 sub = getSub(); | 
|                 for (var i = 0; i < sub.length; ++i) { | 
|                     if (sub[i].tag.isEOC()) { | 
|                         throw new Error("EOC is not supposed to be actual content."); | 
|                     } | 
|                 } | 
|             } | 
|             catch (e) { | 
|                 // but silently ignore when they don't | 
|                 sub = null; | 
|             } | 
|         } | 
|         if (sub === null) { | 
|             if (len === null) { | 
|                 throw new Error("We can't skip over an invalid tag with undefined length at offset " + start); | 
|             } | 
|             stream.pos = start + Math.abs(len); | 
|         } | 
|         return new ASN1(streamStart, header, len, tag, sub); | 
|     }; | 
|     return ASN1; | 
| }()); | 
| export { ASN1 }; | 
| var ASN1Tag = /** @class */ (function () { | 
|     function ASN1Tag(stream) { | 
|         var buf = stream.get(); | 
|         this.tagClass = buf >> 6; | 
|         this.tagConstructed = ((buf & 0x20) !== 0); | 
|         this.tagNumber = buf & 0x1F; | 
|         if (this.tagNumber == 0x1F) { // long tag | 
|             var n = new Int10(); | 
|             do { | 
|                 buf = stream.get(); | 
|                 n.mulAdd(128, buf & 0x7F); | 
|             } while (buf & 0x80); | 
|             this.tagNumber = n.simplify(); | 
|         } | 
|     } | 
|     ASN1Tag.prototype.isUniversal = function () { | 
|         return this.tagClass === 0x00; | 
|     }; | 
|     ASN1Tag.prototype.isEOC = function () { | 
|         return this.tagClass === 0x00 && this.tagNumber === 0x00; | 
|     }; | 
|     return ASN1Tag; | 
| }()); | 
| export { ASN1Tag }; |