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, "<");
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})();