JavaScript generic ASN.1 parser (mirror)
at github-56 220 lines 8.5 kB view raw
1// ASN.1 JavaScript decoder 2// Copyright (c) 2008-2022 Lapo Luchini <lapo@lapo.it> 3 4// Permission to use, copy, modify, and/or distribute this software for any 5// purpose with or without fee is hereby granted, provided that the above 6// copyright notice and this permission notice appear in all copies. 7// 8// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 16(typeof define != 'undefined' ? define : function (factory) { 'use strict'; 17 if (typeof module == 'object') factory(function (name) { return require(name); }); 18 else factory(function (name) { return window[name.substring(2)]; }); 19})(function (require) { 20"use strict"; 21 22var ASN1 = require('./asn1'), 23 oids = require('./oids'), 24 lineLength = 80, 25 contentLength = 8 * lineLength, 26 DOM = { 27 ellipsis: "\u2026", 28 tag: function (tagName, className) { 29 var t = document.createElement(tagName); 30 t.className = className; 31 return t; 32 }, 33 text: function (str) { 34 return document.createTextNode(str); 35 }, 36 space: function () { 37 var t = document.createElement('span'); 38 t.className = 'spaces'; 39 t.innerHTML = ' '; 40 return t; 41 }, 42 breakLines: function (str, length) { 43 var lines = str.split(/\r?\n/), 44 o = ''; 45 for (var i = 0; i < lines.length; ++i) { 46 var line = lines[i]; 47 if (i > 0) o += "\n"; 48 while (line.length > length) { 49 o += line.substring(0, length); 50 o += "\n"; 51 line = line.substring(length); 52 } 53 o += line; 54 } 55 return o; 56 } 57 }; 58 59ASN1.prototype.toDOM = function (spaces) { 60 spaces = spaces || ''; 61 var isOID = (typeof oids === 'object') && (this.tag.isUniversal() && (this.tag.tagNumber == 0x06) || (this.tag.tagNumber == 0x0D)); 62 var node = DOM.tag("div", "node"); 63 node.asn1 = this; 64 var head = DOM.tag("div", "head"); 65 head.innerHTML = "<span class='spaces'>" + spaces + "</span>" + this.typeName().replace(/_/g, " "); 66 var content = this.content(contentLength); 67 if (content !== null) { 68 var preview = DOM.tag("span", "preview"), 69 shortContent; 70 if (isOID) 71 content = content.split('\n', 1)[0]; 72 shortContent = (content.length > lineLength) ? content.substring(0, lineLength) + DOM.ellipsis : content; 73 preview.appendChild(DOM.space()); 74 preview.appendChild(DOM.text(shortContent)); 75 if (isOID) { 76 var oid = oids[content]; 77 if (oid) { 78 if (oid.d) { 79 preview.appendChild(DOM.space()); 80 var oidd = DOM.tag("span", "oid description"); 81 oidd.appendChild(DOM.text(oid.d)); 82 preview.appendChild(oidd); 83 } 84 if (oid.c) { 85 preview.appendChild(DOM.space()); 86 var oidc = DOM.tag("span", "oid comment"); 87 oidc.appendChild(DOM.text("(" + oid.c + ")")); 88 preview.appendChild(oidc); 89 } 90 } 91 } 92 head.appendChild(preview); 93 content = DOM.breakLines(content, lineLength); 94 content = content.replace(/</g, "&lt;"); 95 content = content.replace(/\n/g, "<br>"); 96 } 97 node.appendChild(head); 98 this.node = node; 99 this.head = head; 100 var value = DOM.tag("div", "value"); 101 var s = "Offset: " + this.stream.pos + "<br>"; 102 s += "Length: " + this.header + "+"; 103 if (this.length >= 0) 104 s += this.length; 105 else 106 s += (-this.length) + " (undefined)"; 107 if (this.tag.tagConstructed) 108 s += "<br>(constructed)"; 109 else if ((this.tag.isUniversal() && ((this.tag.tagNumber == 0x03) || (this.tag.tagNumber == 0x04))) && (this.sub !== null)) 110 s += "<br>(encapsulates)"; 111 //TODO if (this.tag.isUniversal() && this.tag.tagNumber == 0x03) s += "Unused bits: " 112 if (content !== null) { 113 s += "<br>Value:<br><b>" + content + "</b>"; 114 if (isOID && oid) { 115 if (oid.d) s += "<br>" + oid.d; 116 if (oid.c) s += "<br>" + oid.c; 117 if (oid.w) s += "<br>(warning!)"; 118 } 119 } 120 value.innerHTML = s; 121 node.appendChild(value); 122 var sub = DOM.tag("div", "sub"); 123 if (this.sub !== null) { 124 spaces += '\xA0 '; 125 for (var i = 0, max = this.sub.length; i < max; ++i) 126 sub.appendChild(this.sub[i].toDOM(spaces)); 127 } 128 node.appendChild(sub); 129 head.onclick = function () { 130 node.className = (node.className == "node collapsed") ? "node" : "node collapsed"; 131 }; 132 return node; 133}; 134ASN1.prototype.fakeHover = function (current) { 135 this.node.className += " hover"; 136 if (current) 137 this.head.className += " hover"; 138}; 139ASN1.prototype.fakeOut = function (current) { 140 var re = / ?hover/; 141 this.node.className = this.node.className.replace(re, ""); 142 if (current) 143 this.head.className = this.head.className.replace(re, ""); 144}; 145ASN1.prototype.toHexDOM_sub = function (node, className, stream, start, end) { 146 if (start >= end) 147 return; 148 var sub = DOM.tag("span", className); 149 sub.appendChild(DOM.text( 150 stream.hexDump(start, end))); 151 node.appendChild(sub); 152}; 153ASN1.prototype.toHexDOM = function (root) { 154 var node = DOM.tag("span", "hex"); 155 if (root === undefined) root = node; 156 this.head.hexNode = node; 157 this.head.onmouseover = function () { this.hexNode.className = "hexCurrent"; }; 158 this.head.onmouseout = function () { this.hexNode.className = "hex"; }; 159 node.asn1 = this; 160 node.onmouseover = function () { 161 var current = !root.selected; 162 if (current) { 163 root.selected = this.asn1; 164 this.className = "hexCurrent"; 165 } 166 this.asn1.fakeHover(current); 167 }; 168 node.onmouseout = function () { 169 var current = (root.selected == this.asn1); 170 this.asn1.fakeOut(current); 171 if (current) { 172 root.selected = null; 173 this.className = "hex"; 174 } 175 }; 176 if (root == node) { 177 var lineStart = this.posStart() & 0xF; 178 if (lineStart != 0) { 179 var skip = DOM.tag("span", "skip"); 180 var skipStr = ''; 181 for (var j = lineStart; j > 0; --j) 182 skipStr += ' '; 183 if (lineStart >= 8) 184 skipStr += ' '; 185 skip.innerText = skipStr; 186 node.appendChild(skip); 187 } 188 } 189 this.toHexDOM_sub(node, "tag", this.stream, this.posStart(), this.posLen()); 190 this.toHexDOM_sub(node, (this.length >= 0) ? "dlen" : "ulen", this.stream, this.posLen(), this.posContent()); 191 if (this.sub === null) { 192 var start = this.posContent(); 193 var end = this.posEnd(); 194 if (end - start < 10 * 16) 195 node.appendChild(DOM.text( 196 this.stream.hexDump(start, end))); 197 else { 198 var end1 = start + 5 * 16 - (start & 0xF); 199 var start2 = end - 16 - (end & 0xF); 200 node.appendChild(DOM.text( 201 this.stream.hexDump(start, end1))); 202 var sub = DOM.tag("span", "skip"); 203 sub.appendChild(DOM.text("\u2026 skipping " + (start2 - end1) + " bytes \u2026\n")); 204 node.appendChild(sub); 205 node.appendChild(DOM.text( 206 this.stream.hexDump(start2, end))); 207 } 208 } else if (this.sub.length > 0) { 209 var first = this.sub[0]; 210 var last = this.sub[this.sub.length - 1]; 211 this.toHexDOM_sub(node, "intro", this.stream, this.posContent(), first.posStart()); 212 for (var i = 0, max = this.sub.length; i < max; ++i) 213 node.appendChild(this.sub[i].toHexDOM(root)); 214 this.toHexDOM_sub(node, "outro", this.stream, last.posEnd(), this.posEnd()); 215 } else 216 this.toHexDOM_sub(node, "outro", this.stream, this.posContent(), this.posEnd()); 217 return node; 218}; 219 220});