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