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 if (isOID)
68 content = content.split('\n', 1)[0];
69 shortContent = (content.length > lineLength) ? content.substring(0, lineLength) + DOM.ellipsis : content;
70 preview.appendChild(DOM.space());
71 preview.appendChild(DOM.text(shortContent));
72 if (isOID) {
73 var oid = oids[content];
74 if (oid) {
75 if (oid.d) {
76 preview.appendChild(DOM.space());
77 var oidd = DOM.tag("span", "oid description");
78 oidd.appendChild(DOM.text(oid.d));
79 preview.appendChild(oidd);
80 }
81 if (oid.c) {
82 preview.appendChild(DOM.space());
83 var oidc = DOM.tag("span", "oid comment");
84 oidc.appendChild(DOM.text("(" + oid.c + ")"));
85 preview.appendChild(oidc);
86 }
87 }
88 }
89 head.appendChild(preview);
90 content = DOM.breakLines(content, lineLength);
91 content = content.replace(/</g, "<");
92 content = content.replace(/\n/g, "<br>");
93 }
94 node.appendChild(head);
95 this.node = node;
96 this.head = head;
97 var value = DOM.tag("div", "value");
98 var s = "Offset: " + this.stream.pos + "<br>";
99 s += "Length: " + this.header + "+";
100 if (this.length >= 0)
101 s += this.length;
102 else
103 s += (-this.length) + " (undefined)";
104 if (this.tag.tagConstructed)
105 s += "<br>(constructed)";
106 else if ((this.tag.isUniversal() && ((this.tag.tagNumber == 0x03) || (this.tag.tagNumber == 0x04))) && (this.sub !== null))
107 s += "<br>(encapsulates)";
108 //TODO if (this.tag.isUniversal() && this.tag.tagNumber == 0x03) s += "Unused bits: "
109 if (content !== null) {
110 s += "<br>Value:<br><b>" + content + "</b>";
111 if (isOID && oid) {
112 if (oid.d) s += "<br>" + oid.d;
113 if (oid.c) s += "<br>" + oid.c;
114 if (oid.w) s += "<br>(warning!)";
115 }
116 }
117 value.innerHTML = s;
118 node.appendChild(value);
119 var sub = DOM.tag("div", "sub");
120 if (this.sub !== null) {
121 spaces += '\xA0 ';
122 for (var i = 0, max = this.sub.length; i < max; ++i)
123 sub.appendChild(this.sub[i].toDOM(spaces));
124 }
125 node.appendChild(sub);
126 head.onclick = function () {
127 node.className = (node.className == "node collapsed") ? "node" : "node collapsed";
128 };
129 return node;
130};
131ASN1.prototype.fakeHover = function (current) {
132 this.node.className += " hover";
133 if (current)
134 this.head.className += " hover";
135};
136ASN1.prototype.fakeOut = function (current) {
137 var re = / ?hover/;
138 this.node.className = this.node.className.replace(re, "");
139 if (current)
140 this.head.className = this.head.className.replace(re, "");
141};
142ASN1.prototype.toHexDOM_sub = function (node, className, stream, start, end) {
143 if (start >= end)
144 return;
145 var sub = DOM.tag("span", className);
146 sub.appendChild(DOM.text(
147 stream.hexDump(start, end)));
148 node.appendChild(sub);
149};
150ASN1.prototype.toHexDOM = function (root) {
151 var node = DOM.tag("span", "hex");
152 if (root === undefined) root = node;
153 this.head.hexNode = node;
154 this.head.onmouseover = function () { this.hexNode.className = "hexCurrent"; };
155 this.head.onmouseout = function () { this.hexNode.className = "hex"; };
156 node.asn1 = this;
157 node.onmouseover = function () {
158 var current = !root.selected;
159 if (current) {
160 root.selected = this.asn1;
161 this.className = "hexCurrent";
162 }
163 this.asn1.fakeHover(current);
164 };
165 node.onmouseout = function () {
166 var current = (root.selected == this.asn1);
167 this.asn1.fakeOut(current);
168 if (current) {
169 root.selected = null;
170 this.className = "hex";
171 }
172 };
173 this.toHexDOM_sub(node, "tag", this.stream, this.posStart(), this.posLen());
174 this.toHexDOM_sub(node, (this.length >= 0) ? "dlen" : "ulen", this.stream, this.posLen(), this.posContent());
175 if (this.sub === null) {
176 var start = this.posContent();
177 var end = this.posEnd();
178 if (end - start < 10 * 16)
179 node.appendChild(DOM.text(
180 this.stream.hexDump(start, end)));
181 else {
182 var end1 = start + 5 * 16 - (start & 0xF);
183 var start2 = end - 16 - (end & 0xF);
184 node.appendChild(DOM.text(
185 this.stream.hexDump(start, end1)));
186 var sub = DOM.tag("span", "skip");
187 sub.appendChild(DOM.text("\u2026 skipping " + (start2 - end1) + " bytes \u2026\n"));
188 node.appendChild(sub);
189 node.appendChild(DOM.text(
190 this.stream.hexDump(start2, end)));
191 }
192 } else if (this.sub.length > 0) {
193 var first = this.sub[0];
194 var last = this.sub[this.sub.length - 1];
195 this.toHexDOM_sub(node, "intro", this.stream, this.posContent(), first.posStart());
196 for (var i = 0, max = this.sub.length; i < max; ++i)
197 node.appendChild(this.sub[i].toHexDOM(root));
198 this.toHexDOM_sub(node, "outro", this.stream, last.posEnd(), this.posEnd());
199 } else
200 this.toHexDOM_sub(node, "outro", this.stream, this.posContent(), this.posEnd());
201 return node;
202};
203
204})();