JavaScript generic ASN.1 parser (mirror)
at github-19 205 lines 8.0 kB view raw
1// ASN.1 JavaScript decoder 2// Copyright (c) 2008-2018 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(function (undefined) { 17"use strict"; 18 19var ASN1 = (typeof module !== 'undefined') ? require('./asn1.js') : window.ASN1, 20 oids = (typeof module !== 'undefined') ? require('./oids.js') : window.oids, 21 lineLength = 80, 22 contentLength = 8 * lineLength, 23 DOM = { 24 ellipsis: "\u2026", 25 tag: function (tagName, className) { 26 var t = document.createElement(tagName); 27 t.className = className; 28 return t; 29 }, 30 text: function (str) { 31 return document.createTextNode(str); 32 }, 33 space: function () { 34 var t = document.createElement('span'); 35 t.className = 'spaces'; 36 t.innerHTML = ' '; 37 return t; 38 }, 39 breakLines: function (str, length) { 40 var lines = str.split(/\r?\n/), 41 o = ''; 42 for (var i = 0; i < lines.length; ++i) { 43 var line = lines[i]; 44 if (i > 0) o += "\n"; 45 while (line.length > length) { 46 o += line.substring(0, length); 47 o += "\n"; 48 line = line.substring(length); 49 } 50 o += line; 51 } 52 return o; 53 } 54 }; 55 56ASN1.prototype.toDOM = function (spaces) { 57 spaces = spaces || ''; 58 var isOID = (typeof oids === 'object') && (this.tag.isUniversal() && (this.tag.tagNumber == 0x06)); 59 var node = DOM.tag("div", "node"); 60 node.asn1 = this; 61 var head = DOM.tag("div", "head"); 62 head.innerHTML = "<span class='spaces'>" + spaces + "</span>" + this.typeName().replace(/_/g, " "); 63 var content = this.content(contentLength); 64 if (content !== null) { 65 var preview = DOM.tag("span", "preview"), 66 shortContent; 67 content = String(content); // it might be a number 68 if (isOID) 69 content = content.split('\n', 1)[0]; 70 shortContent = (content.length > lineLength) ? content.substring(0, lineLength) + DOM.ellipsis : content; 71 preview.appendChild(DOM.space()); 72 preview.appendChild(DOM.text(shortContent)); 73 if (isOID) { 74 var oid = oids[content]; 75 if (oid) { 76 if (oid.d) { 77 preview.appendChild(DOM.space()); 78 var oidd = DOM.tag("span", "oid description"); 79 oidd.appendChild(DOM.text(oid.d)); 80 preview.appendChild(oidd); 81 } 82 if (oid.c) { 83 preview.appendChild(DOM.space()); 84 var oidc = DOM.tag("span", "oid comment"); 85 oidc.appendChild(DOM.text("(" + oid.c + ")")); 86 preview.appendChild(oidc); 87 } 88 } 89 } 90 head.appendChild(preview); 91 content = DOM.breakLines(content, lineLength); 92 content = content.replace(/</g, "&lt;"); 93 content = content.replace(/\n/g, "<br>"); 94 } 95 node.appendChild(head); 96 this.node = node; 97 this.head = head; 98 var value = DOM.tag("div", "value"); 99 var s = "Offset: " + this.stream.pos + "<br>"; 100 s += "Length: " + this.header + "+"; 101 if (this.length >= 0) 102 s += this.length; 103 else 104 s += (-this.length) + " (undefined)"; 105 if (this.tag.tagConstructed) 106 s += "<br>(constructed)"; 107 else if ((this.tag.isUniversal() && ((this.tag.tagNumber == 0x03) || (this.tag.tagNumber == 0x04))) && (this.sub !== null)) 108 s += "<br>(encapsulates)"; 109 //TODO if (this.tag.isUniversal() && this.tag.tagNumber == 0x03) s += "Unused bits: " 110 if (content !== null) { 111 s += "<br>Value:<br><b>" + content + "</b>"; 112 if (isOID && oid) { 113 if (oid.d) s += "<br>" + oid.d; 114 if (oid.c) s += "<br>" + oid.c; 115 if (oid.w) s += "<br>(warning!)"; 116 } 117 } 118 value.innerHTML = s; 119 node.appendChild(value); 120 var sub = DOM.tag("div", "sub"); 121 if (this.sub !== null) { 122 spaces += '\xA0 '; 123 for (var i = 0, max = this.sub.length; i < max; ++i) 124 sub.appendChild(this.sub[i].toDOM(spaces)); 125 } 126 node.appendChild(sub); 127 head.onclick = function () { 128 node.className = (node.className == "node collapsed") ? "node" : "node collapsed"; 129 }; 130 return node; 131}; 132ASN1.prototype.fakeHover = function (current) { 133 this.node.className += " hover"; 134 if (current) 135 this.head.className += " hover"; 136}; 137ASN1.prototype.fakeOut = function (current) { 138 var re = / ?hover/; 139 this.node.className = this.node.className.replace(re, ""); 140 if (current) 141 this.head.className = this.head.className.replace(re, ""); 142}; 143ASN1.prototype.toHexDOM_sub = function (node, className, stream, start, end) { 144 if (start >= end) 145 return; 146 var sub = DOM.tag("span", className); 147 sub.appendChild(DOM.text( 148 stream.hexDump(start, end))); 149 node.appendChild(sub); 150}; 151ASN1.prototype.toHexDOM = function (root) { 152 var node = DOM.tag("span", "hex"); 153 if (root === undefined) root = node; 154 this.head.hexNode = node; 155 this.head.onmouseover = function () { this.hexNode.className = "hexCurrent"; }; 156 this.head.onmouseout = function () { this.hexNode.className = "hex"; }; 157 node.asn1 = this; 158 node.onmouseover = function () { 159 var current = !root.selected; 160 if (current) { 161 root.selected = this.asn1; 162 this.className = "hexCurrent"; 163 } 164 this.asn1.fakeHover(current); 165 }; 166 node.onmouseout = function () { 167 var current = (root.selected == this.asn1); 168 this.asn1.fakeOut(current); 169 if (current) { 170 root.selected = null; 171 this.className = "hex"; 172 } 173 }; 174 this.toHexDOM_sub(node, "tag", this.stream, this.posStart(), this.posStart() + 1); 175 this.toHexDOM_sub(node, (this.length >= 0) ? "dlen" : "ulen", this.stream, this.posStart() + 1, this.posContent()); 176 if (this.sub === null) { 177 var start = this.posContent(); 178 var end = this.posEnd(); 179 if (end - start < 10 * 16) 180 node.appendChild(DOM.text( 181 this.stream.hexDump(start, end))); 182 else { 183 var end1 = start + 5 * 16 - (start & 0xF); 184 var start2 = end - 16 - (end & 0xF); 185 node.appendChild(DOM.text( 186 this.stream.hexDump(start, end1))); 187 var sub = DOM.tag("span", "skip"); 188 sub.appendChild(DOM.text("\u2026 skipping " + (start2 - end1) + " bytes \u2026\n")); 189 node.appendChild(sub); 190 node.appendChild(DOM.text( 191 this.stream.hexDump(start2, end))); 192 } 193 } else if (this.sub.length > 0) { 194 var first = this.sub[0]; 195 var last = this.sub[this.sub.length - 1]; 196 this.toHexDOM_sub(node, "intro", this.stream, this.posContent(), first.posStart()); 197 for (var i = 0, max = this.sub.length; i < max; ++i) 198 node.appendChild(this.sub[i].toHexDOM(root)); 199 this.toHexDOM_sub(node, "outro", this.stream, last.posEnd(), this.posEnd()); 200 } else 201 this.toHexDOM_sub(node, "outro", this.stream, this.posContent(), this.posEnd()); 202 return node; 203}; 204 205})();