From 93afd1d60eaf956127a75315403578bab6110d72 Mon Sep 17 00:00:00 2001 From: Oliver Burgmaier Date: Tue, 15 Nov 2022 21:41:42 +0100 Subject: [PATCH] Adding Darkmode and Dumpcopy Minimalized change to add a darkmode and the feature to copy the selected dump by clicking on the hex view --- dom.js | 12 ++++ index-dark.css | 182 +++++++++++++++++++++++++++++++++++++++++++++++++ index.css | 101 +++++++++++++++++++++++---- index.html | 159 ++++++++++++++++++++++++------------------ index.js | 36 ++++++++++ 5 files changed, 412 insertions(+), 78 deletions(-) create mode 100644 index-dark.css diff --git a/dom.js b/dom.js index eb24327..0cf7692 100644 --- a/dom.js +++ b/dom.js @@ -173,6 +173,13 @@ ASN1.prototype.toHexDOM = function (root) { this.className = "hex"; } }; + node.onclick = function (event) { + const pos = parseInt(this.getAttribute("pos")) + const end = parseInt(this.getAttribute("end")) + const hex = buf2hex(window.derBuffer.subarray(pos, end)) + navigator.clipboard.writeText(hex) + event.stopPropagation() + }; if (root == node) { var lineStart = this.posStart() & 0xF; if (lineStart != 0) { @@ -186,6 +193,8 @@ ASN1.prototype.toHexDOM = function (root) { node.appendChild(skip); } } + node.setAttribute("pos", this.posStart()) + node.setAttribute("end", this.posEnd()) this.toHexDOM_sub(node, "tag", this.stream, this.posStart(), this.posLen()); this.toHexDOM_sub(node, (this.length >= 0) ? "dlen" : "ulen", this.stream, this.posLen(), this.posContent()); if (this.sub === null) { @@ -216,5 +225,8 @@ ASN1.prototype.toHexDOM = function (root) { this.toHexDOM_sub(node, "outro", this.stream, this.posContent(), this.posEnd()); return node; }; +function buf2hex(buffer) { + return [...new Uint8Array(buffer)].map((x) => x.toString(16).padStart(2, "0")).join("") +} }); diff --git a/index-dark.css b/index-dark.css new file mode 100644 index 0000000..1ac9f03 --- /dev/null +++ b/index-dark.css @@ -0,0 +1,182 @@ +:root { + --main-bg-color: #0d1116; + --main-text-color: #f8f8f2; + --headline-text-color: #8be9fd; + --button-border-color: #505050; + --button-bg-color: #303030; + --button-bghover-color: #404040; + --input-border-color: #505050; + --input-bg-color: #0c0e11; + --link-color: #58a6ff; + --link-hover-color: #9b9bea; + --header-bg-color: #161b22; + --page-bg-color: #000000; + --license-bg-color: #4b4a4a; + --license-border-color: black; + --sub-border-color: #383838; + --preview-bg-color: #989797; + --preview-border-color: #b5b3b3; + --dump-bg-color: #0c0e11; + --dump-border-color: #505050; +} +html, body { + background-color: var(--page-bg-color); + color: var(--main-text-color); + font-family: Arial, Helvetica, sans-serif; + text-align: justify; + font-size: 10pt; + margin: 0px; +} +h1 { + font-weight: 200; +} +input, +textarea { + background-color: var(--input-bg-color); + color: var(--main-text-color); + border: 1px solid var(--input-border-color); +} +input[type="button"] { + background-color: var(--button-bg-color); + color: var(--main-text-color); + border: 1px solid var(--button-border-color); +} +input[type="button"]:hover { + background-color: var(--button-bghover-color); +} +::file-selector-button, +::-webkit-file-upload-button { + background-color: var(--button-bg-color); + color: var(--main-text-color); + border: 0px; + border-right: 1px solid var(--button-border-color); +} +::-webkit-file-upload-button:hover { + background-color: var(--button-bghover-color); +} +::file-selector-button:hover { + background-color: var(--button-bghover-color); +} +select { + background-color: var(--input-bg-color); + color: var(--main-text-color); + border: 1px solid var(--input-border-color); + +} +a { + color: var(--link-color); +} +header { + background-color: var(--header-bg-color); + padding: 8px; + padding-left: 16px; +} +#main-page { + background-color: var(--main-bg-color); + border: 0px; + padding: 15px; +} +#help { + margin: 0px; + padding: 4px; + padding-left: 20px; +} +.tt { + font-family: monospace; +} +.license .ref { + position: relative; +} +.license .hidden { + visibility: hidden; + position: absolute; + bottom: 0em; + /*white-space: pre;*/ + background-color: var(--license-bg-color); + border: solid 1px var(--license-border-color); + padding: 2px; + margin-left: 15%; + margin-right: 15%; +} +.license:hover .hidden { + /*display: block;*/ + visibility: visible; +} +.node { + position: relative; +} +.sub { + padding-left: 1.5em; + border-left: solid 1px var(--sub-border-color); +} +.head { + height: 1em; + white-space: nowrap; +} +.node:hover > .head, .node.hover > .head { + color: var(--link-color); + font-weight: bold; +} +.node:hover > .head:hover, .node.hover > .head.hover { + color: var(--link-hover-color); +} +.node.collapsed { + font-style: italic; +} +.node.collapsed > .sub { + display: none; +} +.node.collapsed.hover > .sub { + display: block; +} +.value { + display: none; + position: absolute; + z-index: 2; + top: 1.2em; + left: 0; + background-color: var(--button-bg-color); + border: solid 1px var(--button-border-color); + padding: 2px; +} +.head:hover + .value, .head.hover + .value { + display: block; +} +.preview { + margin-left: 1em; + color: var(--preview-border-color); + font-weight: normal; +} +.preview > .oid { + margin-left: 1em; + color: var(--preview-bg-color); + font-weight: normal; +} +.spaces { + font-size: 0px; +} +#tree { + font-family: monospace; +} +#tree > p { + font-family: Arial, Helvetica, sans-serif; +} +#dump { + z-index: 1; + background-color: var(--dump-bg-color); + border: solid 1px var(--dump-border-color); + font-family: monospace; + white-space: pre; + padding: 5px; +} +#dump .tag { color: #58a6ff; } +#dump .dlen { color: darkcyan; } +#dump .ulen { color: #00b6b6; } +#dump .intro { color: #58a6ff; } +#dump .outro { color: #00b6b6; } +#dump .skip { color: #707070; background-color: #222222; } +#dump .hexCurrent { background-color: #727272; } +#dump .hexCurrent .hex { background-color: #474747; } +#dump .hexCurrent .tag { color: #6db0fc; } +#dump .hexCurrent .dlen { color: #00b6b6; } +#file { display: none; } diff --git a/index.css b/index.css index 23832a9..4b2675b 100644 --- a/index.css +++ b/index.css @@ -1,7 +1,82 @@ +:root { + --main-bg-color: #C0C0C0; + --main-text-color: #000000; + --headline-text-color: #8be9fd; + --button-border-color: #767676; + --button-bg-color: #efefef; + --button-bghover-color: #e5e5e5; + --input-border-color: #767676; + --input-bg-color: #ffffff; + --link-color: darkblue; + --link-hover-color: blue; + --header-bg-color: #999999; + --page-bg-color: #AAAAAA; + --license-bg-color: #D0D0D0; + --license-border-color: white; + --sub-border-color: #E0E0E0; + --preview-bg-color: #808080; + --preview-border-color: #505050; + --dump-bg-color: #C0C0C0; + --dump-border-color: #E0E0E0; +} html, body { - background-color: #C0C0C0; + background-color: var(--page-bg-color); + color: var(--main-text-color); font-family: Arial, Helvetica, sans-serif; text-align: justify; + font-size: 10pt; + margin: 0px; +} +input, +textarea { + background-color: var(--input-bg-color); + color: var(--main-text-color); + border: 1px solid var(--input-border-color); +} +input[type="button"] { + background-color: var(--button-bg-color); + color: var(--main-text-color); + border: 1px solid var(--button-border-color); +} +input[type="button"]:hover { + background-color: var(--button-bghover-color); +} +::file-selector-button, +::-webkit-file-upload-button { + background-color: var(--button-bg-color); + color: var(--main-text-color); + border: 0px; + border-right: 1px solid var(--button-border-color); +} +::-webkit-file-upload-button:hover { + background-color: var(--button-bghover-color); +} +::file-selector-button:hover { + background-color: var(--button-bghover-color); +} +select { + background-color: var(--input-bg-color); + color: var(--main-text-color); + border: 1px solid var(--input-border-color); + +} +a { + color: var(--link-color); +} +header { + background-color: var(--header-bg-color); + padding: 8px; + padding-left: 16px; +} +#main-page { + background-color: var(--main-bg-color); + border: 0px; + padding: 15px; +} +#help { + margin: 0px; + padding: 4px; + padding-left: 20px; } .tt { font-family: monospace; @@ -14,8 +89,8 @@ html, body { position: absolute; bottom: 0em; /*white-space: pre;*/ - background-color: #D0D0D0; - border: solid 1px white; + background-color: var(--license-bg-color); + border: solid 1px var(--license-border-color); padding: 2px; margin-left: 15%; margin-right: 15%; @@ -29,18 +104,18 @@ html, body { } .sub { padding-left: 1.5em; - border-left: solid 1px #E0E0E0; + border-left: solid 1px var(--sub-border-color); } .head { height: 1em; white-space: nowrap; } .node:hover > .head, .node.hover > .head { - color: darkblue; + color: var(--link-color); font-weight: bold; } .node:hover > .head:hover, .node.hover > .head.hover { - color: blue; + color: var(--link-hover-color); } .node.collapsed { font-style: italic; @@ -57,8 +132,8 @@ html, body { z-index: 2; top: 1.2em; left: 0; - background-color: #D0D0D0; - border: solid 1px white; + background-color: var(--button-bg-color); + border: solid 1px var(--button-border-color); padding: 2px; } .head:hover + .value, .head.hover + .value { @@ -66,12 +141,12 @@ html, body { } .preview { margin-left: 1em; - color: #505050; + color: var(--preview-border-color); font-weight: normal; } .preview > .oid { margin-left: 1em; - color: #808080; + color: var(--preview-bg-color); font-weight: normal; } .spaces { @@ -85,11 +160,11 @@ html, body { } #dump { z-index: 1; - background-color: #C0C0C0; - border: solid 1px #E0E0E0; + background-color: var(--dump-bg-color); + border: solid 1px var(--dump-border-color); font-family: monospace; white-space: pre; - padding: 2px; + padding: 5px; } #dump .tag { color: blue; } #dump .dlen { color: darkcyan; } diff --git a/index.html b/index.html index 0048652..2ecf7c7 100644 --- a/index.html +++ b/index.html @@ -4,75 +4,104 @@ ASN.1 JavaScript decoder - + -

ASN.1 JavaScript decoder

-
-
-
-
-
- -
- - - - -
- Examples: - - -
-
-
-

Instructions

-

This page contains a JavaScript generic ASN.1 parser that can decode any valid ASN.1 DER or BER structure whether Base64-encoded (raw base64, PEM armoring and begin-base64 are recognized) or Hex-encoded.

-

This tool can be used online at the address http://lapo.it/asn1js/ or offline, unpacking the ZIP file in a directory and opening index.html in a browser

-

On the left of the page will be printed a tree representing the hierarchical structure, on the right side an hex dump will be shown.
- Hovering on the tree highlights ancestry (the hovered node and all its ancestors get colored) and the position of the hovered node gets highlighted in the hex dump (with header and content in a different colors).
- Clicking a node in the tree will hide its sub-nodes (collapsed nodes can be noticed because they will become italic).

-
-

Copyright

-
+
+

ASN.1 JavaScript decoder

+
+
+ +
+ +
+
+
+
+
+
+
+
+ +

- Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies.
+ + + +

+ + + + + + +
+ +   +
Examples: + + +
+

- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -

-

ASN.1 JavaScript decoder Copyright © 2008-2022 Lapo Luchini; released as opensource under the ISC license.

+ +
+
+
+

Instructions

+

This page contains a JavaScript generic ASN.1 parser that can decode any valid ASN.1 DER or BER structure whether Base64-encoded (raw base64, PEM armoring and begin-base64 are recognized) or Hex-encoded.

+

This tool can be used online at the address http://lapo.it/asn1js/ or offline, unpacking the ZIP file in a directory and opening index.html in a browser

+

On the left of the page will be printed a tree representing the hierarchical structure, on the right side an hex dump will be shown.
+ Hovering on the tree highlights ancestry (the hovered node and all its ancestors get colored) and the position of the hovered node gets highlighted in the hex dump (with header and content in a different colors).
+ Clicking on the highlighted hex dump will copy the value into the clipboard.
+ Clicking a node in the tree will hide its sub-nodes (collapsed nodes can be noticed because they will become italic).

+
+

Copyright

+
+

ASN.1 JavaScript decoder Copyright © 2008-2022 Lapo Luchini; released as opensource under the ISC license.

+
+

OBJECT IDENTIFIER values are recognized using data taken from Peter Gutmann's dumpasn1 program.

+

Links

+
-

OBJECT IDENTIFIER values are recognized using data taken from Peter Gutmann's dumpasn1 program.

-

Links

- -
- - - - - - - + + + + + + + + diff --git a/index.js b/index.js index f3f34de..960847e 100644 --- a/index.js +++ b/index.js @@ -4,6 +4,40 @@ })(function (require) { "use strict"; +// set dark theme depending on os settings +const themeSelect = document.querySelector('#theme-select'); +function setTheme() { + const storedTheme = localStorage.getItem('theme'); + let theme = 'os'; + if (storedTheme) { + theme = storedTheme; + } + + themeSelect.value = theme; + + if (theme == 'os') { + const prefersDarkScheme = window.matchMedia('(prefers-color-scheme: dark)'); + if (prefersDarkScheme.matches) { + theme = 'dark'; + } else { + theme = 'light'; + } + } + + const themeLink = document.querySelector('#theme-link'); + if (theme == 'dark') { + themeLink.href = 'index-dark.css'; + } else { + themeLink.href = 'index.css'; + } +} +setTheme(); +themeSelect.addEventListener('change', function () { + const selectedTheme = themeSelect.value; + localStorage.setItem('theme', selectedTheme); + setTheme(); +}); + var ASN1 = require('./asn1'), Base64 = require('./base64'), Hex = require('./hex'), @@ -30,6 +64,8 @@ function text(el, string) { el.innerText = string; } function decode(der, offset) { + window.derBuffer = der; // store der buffer of asn1 in window to copy it into clipboard on dump click + offset = offset || 0; tree.innerHTML = ''; dump.innerHTML = '';