diff --git a/asn1.js b/asn1.js index d157950..5dde9a3 100644 --- a/asn1.js +++ b/asn1.js @@ -348,6 +348,29 @@ function recurse(el, parser, maxLength) { return d; } +class ASN1Tag { + constructor(stream) { + let buf = stream.get(); + this.tagClass = buf >> 6; + this.tagConstructed = ((buf & 0x20) !== 0); + this.tagNumber = buf & 0x1F; + if (this.tagNumber == 0x1F) { // long tag + let n = new Int10(); + do { + buf = stream.get(); + n.mulAdd(128, buf & 0x7F); + } while (buf & 0x80); + this.tagNumber = n.simplify(); + } + } + isUniversal() { + return this.tagClass === 0x00; + }; + isEOC() { + return this.tagClass === 0x00 && this.tagNumber === 0x00; + }; +} + class ASN1 { constructor(stream, header, length, tag, tagLen, sub) { if (!(tag instanceof ASN1Tag)) throw 'Invalid tag value.'; @@ -519,7 +542,9 @@ class ASN1 { buf = (buf * 256) + stream.get(); return buf; }; - static decode(stream, offset) { + static decode(stream, offset, type = ASN1) { + if (!(type.prototype instanceof ASN1)) + throw 'Must pass a class that extends ASN1'; if (!(stream instanceof Stream)) stream = new Stream(stream, offset || 0); let streamStart = new Stream(stream), @@ -537,14 +562,14 @@ class ASN1 { if (end > stream.enc.length) throw 'Container at offset ' + start + ' has a length of ' + len + ', which is past the end of the stream'; while (stream.pos < end) - sub[sub.length] = ASN1.decode(stream); + sub[sub.length] = type.decode(stream); if (stream.pos != end) throw 'Content size is not correct for container at offset ' + start; } else { // undefined length try { for (;;) { - let s = ASN1.decode(stream); + let s = type.decode(stream); if (s.tag.isEOC()) break; sub[sub.length] = s; @@ -579,34 +604,11 @@ class ASN1 { throw "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, tagLen, sub); + return new type(streamStart, header, len, tag, tagLen, sub); }; } -class ASN1Tag { - constructor(stream) { - let buf = stream.get(); - this.tagClass = buf >> 6; - this.tagConstructed = ((buf & 0x20) !== 0); - this.tagNumber = buf & 0x1F; - if (this.tagNumber == 0x1F) { // long tag - let n = new Int10(); - do { - buf = stream.get(); - n.mulAdd(128, buf & 0x7F); - } while (buf & 0x80); - this.tagNumber = n.simplify(); - } - } - isUniversal() { - return this.tagClass === 0x00; - }; - isEOC() { - return this.tagClass === 0x00 && this.tagNumber === 0x00; - }; -} - return ASN1; }); diff --git a/dom.js b/dom.js index 6a474c3..f293eb4 100644 --- a/dom.js +++ b/dom.js @@ -14,8 +14,8 @@ // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. (typeof define != 'undefined' ? define : function (factory) { 'use strict'; - if (typeof module == 'object') factory(function (name) { return require(name); }); - else factory(function (name) { return window[name.substring(2)]; }); + if (typeof module == 'object') module.exports = factory(function (name) { return require(name); }); + else window.dom = factory(function (name) { return window[name.substring(2)]; }); })(function (require) { 'use strict'; @@ -57,166 +57,175 @@ const } }; -ASN1.prototype.toDOM = function (spaces) { - spaces = spaces || ''; - let isOID = (typeof oids === 'object') && (this.tag.isUniversal() && (this.tag.tagNumber == 0x06) || (this.tag.tagNumber == 0x0D)); - let node = DOM.tag('div', 'node'); - node.asn1 = this; - let head = DOM.tag('div', 'head'); - head.innerHTML = "" + spaces + '' + this.typeName().replace(/_/g, ' '); - let content = this.content(contentLength); - let oid; - if (content !== null) { - let preview = DOM.tag('span', 'preview'), - shortContent; - if (isOID) - content = content.split('\n', 1)[0]; - shortContent = (content.length > lineLength) ? content.substring(0, lineLength) + DOM.ellipsis : content; - preview.appendChild(DOM.space()); - preview.appendChild(DOM.text(shortContent)); - if (isOID) { - oid = oids[content]; - if (oid) { - if (oid.d) { - preview.appendChild(DOM.space()); - let oidd = DOM.tag('span', 'oid description'); - oidd.appendChild(DOM.text(oid.d)); - preview.appendChild(oidd); - } - if (oid.c) { - preview.appendChild(DOM.space()); - let oidc = DOM.tag('span', 'oid comment'); - oidc.appendChild(DOM.text('(' + oid.c + ')')); - preview.appendChild(oidc); +class ASN1DOM extends ASN1 { + + toDOM(spaces) { + spaces = spaces || ''; + let isOID = (typeof oids === 'object') && (this.tag.isUniversal() && (this.tag.tagNumber == 0x06) || (this.tag.tagNumber == 0x0D)); + let node = DOM.tag('div', 'node'); + node.asn1 = this; + let head = DOM.tag('div', 'head'); + head.innerHTML = "" + spaces + '' + this.typeName().replace(/_/g, ' '); + let content = this.content(contentLength); + let oid; + if (content !== null) { + let preview = DOM.tag('span', 'preview'), + shortContent; + if (isOID) + content = content.split('\n', 1)[0]; + shortContent = (content.length > lineLength) ? content.substring(0, lineLength) + DOM.ellipsis : content; + preview.appendChild(DOM.space()); + preview.appendChild(DOM.text(shortContent)); + if (isOID) { + oid = oids[content]; + if (oid) { + if (oid.d) { + preview.appendChild(DOM.space()); + let oidd = DOM.tag('span', 'oid description'); + oidd.appendChild(DOM.text(oid.d)); + preview.appendChild(oidd); + } + if (oid.c) { + preview.appendChild(DOM.space()); + let oidc = DOM.tag('span', 'oid comment'); + oidc.appendChild(DOM.text('(' + oid.c + ')')); + preview.appendChild(oidc); + } } } + head.appendChild(preview); + content = DOM.breakLines(content, lineLength); + content = content.replace(/'); } - head.appendChild(preview); - content = DOM.breakLines(content, lineLength); - content = content.replace(/'); - } - node.appendChild(head); - this.node = node; - this.head = head; - let value = DOM.tag('div', 'value'); - let s = 'Offset: ' + this.stream.pos + '
'; - s += 'Length: ' + this.header + '+'; - if (this.length >= 0) - s += this.length; - else - s += (-this.length) + ' (undefined)'; - if (this.tag.tagConstructed) - s += '
(constructed)'; - else if ((this.tag.isUniversal() && ((this.tag.tagNumber == 0x03) || (this.tag.tagNumber == 0x04))) && (this.sub !== null)) - s += '
(encapsulates)'; - //TODO if (this.tag.isUniversal() && this.tag.tagNumber == 0x03) s += "Unused bits: " - if (content !== null) { - s += '
Value:
' + content + ''; - if (isOID && oid) { - if (oid.d) s += '
' + oid.d; - if (oid.c) s += '
' + oid.c; - if (oid.w) s += '
(warning!)'; + node.appendChild(head); + this.node = node; + this.head = head; + let value = DOM.tag('div', 'value'); + let s = 'Offset: ' + this.stream.pos + '
'; + s += 'Length: ' + this.header + '+'; + if (this.length >= 0) + s += this.length; + else + s += (-this.length) + ' (undefined)'; + if (this.tag.tagConstructed) + s += '
(constructed)'; + else if ((this.tag.isUniversal() && ((this.tag.tagNumber == 0x03) || (this.tag.tagNumber == 0x04))) && (this.sub !== null)) + s += '
(encapsulates)'; + //TODO if (this.tag.isUniversal() && this.tag.tagNumber == 0x03) s += "Unused bits: " + if (content !== null) { + s += '
Value:
' + content + ''; + if (isOID && oid) { + if (oid.d) s += '
' + oid.d; + if (oid.c) s += '
' + oid.c; + if (oid.w) s += '
(warning!)'; + } } - } - value.innerHTML = s; - node.appendChild(value); - let sub = DOM.tag('div', 'sub'); - if (this.sub !== null) { - spaces += '\xA0 '; - for (let i = 0, max = this.sub.length; i < max; ++i) - sub.appendChild(this.sub[i].toDOM(spaces)); - } - node.appendChild(sub); - head.onclick = function () { - node.className = (node.className == 'node collapsed') ? 'node' : 'node collapsed'; - }; - return node; -}; -ASN1.prototype.fakeHover = function (current) { - this.node.className += ' hover'; - if (current) - this.head.className += ' hover'; -}; -ASN1.prototype.fakeOut = function (current) { - let re = / ?hover/; - this.node.className = this.node.className.replace(re, ''); - if (current) - this.head.className = this.head.className.replace(re, ''); -}; -ASN1.prototype.toHexDOM_sub = function (node, className, stream, start, end) { - if (start >= end) - return; - let sub = DOM.tag('span', className); - sub.appendChild(DOM.text( - stream.hexDump(start, end))); - node.appendChild(sub); -}; -ASN1.prototype.toHexDOM = function (root) { - let node = DOM.tag('span', 'hex'); - if (root === undefined) root = node; - this.head.hexNode = node; - this.head.onmouseover = function () { this.hexNode.className = 'hexCurrent'; }; - this.head.onmouseout = function () { this.hexNode.className = 'hex'; }; - node.asn1 = this; - node.onmouseover = function () { - let current = !root.selected; - if (current) { - root.selected = this.asn1; - this.className = 'hexCurrent'; + value.innerHTML = s; + node.appendChild(value); + let sub = DOM.tag('div', 'sub'); + if (this.sub !== null) { + spaces += '\xA0 '; + for (let i = 0, max = this.sub.length; i < max; ++i) + sub.appendChild(this.sub[i].toDOM(spaces)); } - this.asn1.fakeHover(current); + node.appendChild(sub); + head.onclick = function () { + node.className = (node.className == 'node collapsed') ? 'node' : 'node collapsed'; + }; + return node; }; - node.onmouseout = function () { - let current = (root.selected == this.asn1); - this.asn1.fakeOut(current); - if (current) { - root.selected = null; - this.className = 'hex'; - } + fakeHover(current) { + this.node.className += ' hover'; + if (current) + this.head.className += ' hover'; + }; + fakeOut(current) { + let re = / ?hover/; + this.node.className = this.node.className.replace(re, ''); + if (current) + this.head.className = this.head.className.replace(re, ''); }; - if (root == node) { - let lineStart = this.posStart() & 0xF; - if (lineStart != 0) { - let skip = DOM.tag('span', 'skip'); - let skipStr = ''; - for (let j = lineStart; j > 0; --j) - skipStr += ' '; - if (lineStart >= 8) - skipStr += ' '; - skip.innerText = skipStr; - node.appendChild(skip); + toHexDOM_sub(node, className, stream, start, end) { + if (start >= end) + return; + let sub = DOM.tag('span', className); + sub.appendChild(DOM.text( + stream.hexDump(start, end))); + node.appendChild(sub); + }; + toHexDOM(root) { + let node = DOM.tag('span', 'hex'); + if (root === undefined) root = node; + this.head.hexNode = node; + this.head.onmouseover = function () { this.hexNode.className = 'hexCurrent'; }; + this.head.onmouseout = function () { this.hexNode.className = 'hex'; }; + node.asn1 = this; + node.onmouseover = function () { + let current = !root.selected; + if (current) { + root.selected = this.asn1; + this.className = 'hexCurrent'; + } + this.asn1.fakeHover(current); + }; + node.onmouseout = function () { + let current = (root.selected == this.asn1); + this.asn1.fakeOut(current); + if (current) { + root.selected = null; + this.className = 'hex'; + } + }; + if (root == node) { + let lineStart = this.posStart() & 0xF; + if (lineStart != 0) { + let skip = DOM.tag('span', 'skip'); + let skipStr = ''; + for (let j = lineStart; j > 0; --j) + skipStr += ' '; + if (lineStart >= 8) + skipStr += ' '; + skip.innerText = skipStr; + node.appendChild(skip); + } } + this.toHexDOM_sub(node, 'tag', this.stream, this.posStart(), this.posLen()); + this.toHexDOM_sub(node, (this.length >= 0) ? 'dlen' : 'ulen', this.stream, this.posLen(), this.posContent()); + if (this.sub === null) { + let start = this.posContent(); + let end = this.posEnd(); + if (end - start < 10 * 16) + node.appendChild(DOM.text( + this.stream.hexDump(start, end))); + else { + let end1 = start + 5 * 16 - (start & 0xF); + let start2 = end - 16 - (end & 0xF); + node.appendChild(DOM.text( + this.stream.hexDump(start, end1))); + let sub = DOM.tag('span', 'skip'); + sub.appendChild(DOM.text('\u2026 skipping ' + (start2 - end1) + ' bytes \u2026\n')); + node.appendChild(sub); + node.appendChild(DOM.text( + this.stream.hexDump(start2, end))); + } + } else if (this.sub.length > 0) { + let first = this.sub[0]; + let last = this.sub[this.sub.length - 1]; + this.toHexDOM_sub(node, 'intro', this.stream, this.posContent(), first.posStart()); + for (let i = 0, max = this.sub.length; i < max; ++i) + node.appendChild(this.sub[i].toHexDOM(root)); + this.toHexDOM_sub(node, 'outro', this.stream, last.posEnd(), this.posEnd()); + } else + this.toHexDOM_sub(node, 'outro', this.stream, this.posContent(), this.posEnd()); + return node; + }; + static decode(stream, offset) { + return ASN1.decode(stream, offset, ASN1DOM); } - this.toHexDOM_sub(node, 'tag', this.stream, this.posStart(), this.posLen()); - this.toHexDOM_sub(node, (this.length >= 0) ? 'dlen' : 'ulen', this.stream, this.posLen(), this.posContent()); - if (this.sub === null) { - let start = this.posContent(); - let end = this.posEnd(); - if (end - start < 10 * 16) - node.appendChild(DOM.text( - this.stream.hexDump(start, end))); - else { - let end1 = start + 5 * 16 - (start & 0xF); - let start2 = end - 16 - (end & 0xF); - node.appendChild(DOM.text( - this.stream.hexDump(start, end1))); - let sub = DOM.tag('span', 'skip'); - sub.appendChild(DOM.text('\u2026 skipping ' + (start2 - end1) + ' bytes \u2026\n')); - node.appendChild(sub); - node.appendChild(DOM.text( - this.stream.hexDump(start2, end))); - } - } else if (this.sub.length > 0) { - let first = this.sub[0]; - let last = this.sub[this.sub.length - 1]; - this.toHexDOM_sub(node, 'intro', this.stream, this.posContent(), first.posStart()); - for (let i = 0, max = this.sub.length; i < max; ++i) - node.appendChild(this.sub[i].toHexDOM(root)); - this.toHexDOM_sub(node, 'outro', this.stream, last.posEnd(), this.posEnd()); - } else - this.toHexDOM_sub(node, 'outro', this.stream, this.posContent(), this.posEnd()); - return node; -}; + +} + +return ASN1DOM; });