|
|
@@ -0,0 +1,565 @@
|
|
|
+// 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 };
|