···11-ASN.1 JavaScript decoder Copyright (c) 2008-2021 Lapo Luchini <lapo@lapo.it>
11+ISC License
22+33+Copyright (c) 2008-2025 Lapo Luchini <lapo@lapo.it>
2435Permission to use, copy, modify, and/or distribute this software for any
46purpose with or without fee is hereby granted, provided that the above
···1113WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1214ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1315OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1414-
+54-21
README.md
···3344asn1js is a JavaScript generic ASN.1 parser/decoder that can decode any valid ASN.1 DER or BER structures.
5566-An example page that can decode Base64-encoded (raw base64, PEM armoring and `begin-base64` are recognized) or Hex-encoded (or local files with some browsers) is included and can be used both [online on the official website](https://lapo.it/asn1js/) or [offline (ZIP file)](https://lapo.it/asn1js/asn1js.zip).
66+An example page that can decode Base64-encoded (raw base64, PEM armoring and `begin-base64` are recognized) or Hex-encoded (or local files with some browsers) is included and can be used both [online on the official website](https://asn1js.eu/) or [offline (ZIP file)](https://lapo.it/asn1js/asn1js.zip) by opening `index-local.html`.
7788-Usage with `npm` / `yarn`
99--------------------------
88+Usage with `nodejs`
99+-------------------
10101111This package can be installed with either npm or yarn via the following commands:
12121313```sh
1414npm install @lapo/asn1js
1515-# or with yarn
1515+# or other tools
1616+pnpm install @lapo/asn1js
1617yarn add @lapo/asn1js
1718```
18191919-Assuming a standard javascript bundler is setup you can import it like so:
2020+You can import the classes like this:
20212122```js
2222-const ASN1 = require('@lapo/asn1js');
2323-// or with ES modules
2424-import ASN1 from '@lapo/asn1js';
2323+import { ASN1 } from '@lapo/asn1js';
2524```
26252726A submodule of this package can also be imported:
28272928```js
3030-const Hex = require('@lapo/asn1js/hex');
3131-// or with ES modules
3232-import Hex from '@lapo/asn1js/hex';
2929+import { Hex } from '@lapo/asn1js/hex.js';
3030+```
3131+3232+If your code is still not using ES6 Modules (and is using CommonJS) you can `require` it normally [since NodeJS 22](https://joyeecheung.github.io/blog/2024/03/18/require-esm-in-node-js/) (with parameter `--experimental-require-module`):
3333+3434+```js
3535+const
3636+ { ASN1 } = require('@lapo/asn1js'),
3737+ { Hex } = require('@lapo/asn1js/hex.js');
3838+console.log(ASN1.decode(Hex.decode('06032B6570')).content());
3939+```
4040+4141+On older NodeJS you instead need to use async `import`:
4242+4343+```js
4444+async function main() {
4545+ const
4646+ { ASN1 } = await import('@lapo/asn1js'),
4747+ { Hex } = await import('@lapo/asn1js/hex.js');
4848+ console.log(ASN1.decode(Hex.decode('06032B6570')).content());
4949+}
5050+main();
3351```
34523535-Usage with RequireJS
5353+Usage on the web
3654--------------------
37553838-Can be [tested on JSFiddle](https://jsfiddle.net/lapo/tmdq35ug/).
5656+Can be [tested on JSFiddle](https://jsfiddle.net/lapo/y6t2wo7q/).
39574058```html
4141-<script type="text/javascript" src="https://unpkg.com/requirejs/require.js"></script>
4259<script>
4343-require([
4444- 'https://unpkg.com/@lapo/asn1js/asn1.js',
4545- 'https://unpkg.com/@lapo/asn1js/hex.js'
4646-], function(ASN1, Hex) {
4747- document.body.innerText = ASN1.decode(Hex.decode('06032B6570')).content();
4848-});
6060+import { ASN1 } from 'https://unpkg.com/@lapo/asn1js@2.0.0/asn1.js';
6161+import { Hex } from 'https://unpkg.com/@lapo/asn1js@2.0.0/hex.js';
6262+6363+document.body.innerText = ASN1.decode(Hex.decode('06032B6570')).content();
4964</script>
5065```
51666767+Local usage
6868+--------------------
6969+7070+Since unfortunately ESM modules are not working on `file:` protocol due to [CORS issues](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules#other_differences_between_modules_and_standard_scripts), there is a bundled [single-file version working locally](https://asn1js.eu/index-local.html). It doesn't work online (due to [CSP](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) restrictions about inline content) but can be saved locally and opened in a browser.
7171+7272+Usage from CLI
7373+--------------------
7474+7575+You can dump an ASN.1 structure from the command line using the following command (no need to even install it):
7676+7777+```sh
7878+npx @lapo/asn1js ed25519.cer
7979+```
8080+5281ISC license
5382-----------
54835555-ASN.1 JavaScript decoder Copyright (c) 2008-2020 Lapo Luchini <lapo@lapo.it>
8484+ASN.1 JavaScript decoder Copyright (c) 2008-2025 Lapo Luchini <lapo@lapo.it>
56855786Permission 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.
5887···6594- BMPString support added by [Felipe Gasper](https://github.com/FGasper)
6695- extended tag support added by [Pรฉter Budai](https://www.peterbudai.eu/)
6796- patches by [Gergely Nagy](https://github.com/ngg)
9797+- Relative OID support added by [Mistial Developer](https://github.com/mistial-dev)
9898+- dark mode and other UI improvements by [Oliver Burgmaier](https://github.com/olibu/)
9999+- patches by [Nicolai Sรธborg](https://github.com/NicolaiSoeborg)
6810069101links
70102-----
7110372104- [official website](https://lapo.it/asn1js/)
105105+- [dedicated domain](https://asn1js.eu/)
73106- [InDefero tracker](http://idf.lapo.it/p/asn1js/)
74107- [GitHub mirror](https://github.com/lapo-luchini/asn1js)
75108- [Ohloh code stats](https://www.openhub.net/p/asn1js)
+580-475
asn1.js
···11// ASN.1 JavaScript decoder
22-// Copyright (c) 2008-2021 Lapo Luchini <lapo@lapo.it>
22+// Copyright (c) 2008 Lapo Luchini <lapo@lapo.it>
3344// Permission to use, copy, modify, and/or distribute this software for any
55// purpose with or without fee is hereby granted, provided that the above
66// copyright notice and this permission notice appear in all copies.
77-//
77+//
88// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
99// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1010// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
···1313// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1414// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15151616-(typeof define != 'undefined' ? define : function (factory) { 'use strict';
1717- if (typeof module == 'object') module.exports = factory(function (name) { return require(name); });
1818- else window.asn1 = factory(function (name) { return window[name.substring(2)]; });
1919-})(function (require) {
2020-"use strict";
1616+import { Int10 } from './int10.js';
1717+import { oids } from './oids.js';
21182222-var Int10 = require('./int10'),
2323- oids = require('./oids'),
2424- ellipsis = "\u2026",
2525- reTimeS = /^(\d\d)(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])([01]\d|2[0-3])(?:([0-5]\d)(?:([0-5]\d)(?:[.,](\d{1,3}))?)?)?(Z|[-+](?:[0]\d|1[0-2])([0-5]\d)?)?$/,
2626- reTimeL = /^(\d\d\d\d)(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])([01]\d|2[0-3])(?:([0-5]\d)(?:([0-5]\d)(?:[.,](\d{1,3}))?)?)?(Z|[-+](?:[0]\d|1[0-2])([0-5]\d)?)?$/;
1919+const
2020+ ellipsis = '\u2026',
2121+ reTimeS = /^(\d\d)(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])([01]\d|2[0-3])(?:([0-5]\d)(?:([0-5]\d)(?:[.,](\d{1,3}))?)?)?(Z|(-(?:0\d|1[0-2])|[+](?:0\d|1[0-4]))([0-5]\d)?)?$/,
2222+ reTimeL = /^(\d\d\d\d)(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])([01]\d|2[0-3])(?:([0-5]\d)(?:([0-5]\d)(?:[.,](\d{1,3}))?)?)?(Z|(-(?:0\d|1[0-2])|[+](?:0\d|1[0-4]))([0-5]\d)?)?$/,
2323+ hexDigits = '0123456789ABCDEF',
2424+ b64Std = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
2525+ b64URL = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_',
2626+ tableT61 = [
2727+ ['', ''],
2828+ ['AEIOUaeiou', 'รรรรรร รจรฌรฒรน'], // Grave
2929+ ['ACEILNORSUYZacegilnorsuyz', 'รฤรรฤนลรลลรรลนรกฤรฉฤฃรญฤบลรณลลรบรฝลบ'], // Acute
3030+ ['ACEGHIJOSUWYaceghijosuwy', 'รฤรฤฤครฤดรลรลดลถรขฤรชฤฤฅรฎฤตรดลรปลตลท'], // Circumflex
3131+ ['AINOUainou', 'รฤจรรลจรฃฤฉรฑรตลฉ'], // Tilde
3232+ ['AEIOUaeiou', 'ฤฤฤชลลชฤฤฤซลลซ'], // Macron
3333+ ['AGUagu', 'ฤฤลฌฤฤลญ'], // Breve
3434+ ['CEGIZcegz', 'ฤฤฤ ฤฐลปฤฤฤกลผ'], // Dot
3535+ ['AEIOUYaeiouy', 'รรรรรลธรครซรฏรถรผรฟ'], // Umlaut or diรฆresis
3636+ ['', ''],
3737+ ['AUau', 'ร ลฎรฅลฏ'], // Ring
3838+ ['CGKLNRSTcklnrst', 'รฤขฤถฤปล ลลลขรงฤทฤผลลลลฃ'], // Cedilla
3939+ ['', ''],
4040+ ['OUou', 'ลลฐลลฑ'], // Double Acute
4141+ ['AEIUaeiu', 'ฤฤฤฎลฒฤ ฤฤฏลณ'], // Ogonek
4242+ ['CDELNRSTZcdelnrstz', 'ฤฤฤฤฝลลล ลคลฝฤฤฤฤพลลลกลฅลพ'], // Caron
4343+ ];
27442845function stringCut(str, len) {
2946 if (str.length > len)
···3148 return str;
3249}
33503434-function Stream(enc, pos) {
3535- if (enc instanceof Stream) {
3636- this.enc = enc.enc;
3737- this.pos = enc.pos;
3838- } else {
3939- // enc should be an array or a binary string
4040- this.enc = enc;
4141- this.pos = pos;
5151+function checkPrintable(s) {
5252+ let i, v;
5353+ for (i = 0; i < s.length; ++i) {
5454+ v = s.charCodeAt(i);
5555+ if (v < 32 && v != 9 && v != 10 && v != 13) // [\t\r\n] are (kinda) printable
5656+ throw new Error('Unprintable character at index ' + i + ' (code ' + s.str.charCodeAt(i) + ')');
4257 }
4358}
4444-Stream.prototype.get = function (pos) {
4545- if (pos === undefined)
4646- pos = this.pos++;
4747- if (pos >= this.enc.length)
4848- throw 'Requesting byte offset ' + pos + ' on a stream of length ' + this.enc.length;
4949- return (typeof this.enc == "string") ? this.enc.charCodeAt(pos) : this.enc[pos];
5050-};
5151-Stream.prototype.hexDigits = "0123456789ABCDEF";
5252-Stream.prototype.hexByte = function (b) {
5353- return this.hexDigits.charAt((b >> 4) & 0xF) + this.hexDigits.charAt(b & 0xF);
5454-};
5555-Stream.prototype.hexDump = function (start, end, raw) {
5656- var s = "";
5757- for (var i = start; i < end; ++i) {
5858- s += this.hexByte(this.get(i));
5959- if (raw !== true)
6060- switch (i & 0xF) {
6161- case 0x7: s += " "; break;
6262- case 0xF: s += "\n"; break;
6363- default: s += " ";
6464- }
5959+6060+/** Class to manage a stream of bytes, with a zero-copy approach.
6161+ * It uses an existing array or binary string and advances a position index. */
6262+export class Stream {
6363+6464+ /**
6565+ * @param {Stream|array|string} enc data (will not be copied)
6666+ * @param {?number} pos starting position (mandatory when `end` is not a Stream)
6767+ */
6868+ constructor(enc, pos) {
6969+ if (enc instanceof Stream) {
7070+ this.enc = enc.enc;
7171+ this.pos = enc.pos;
7272+ } else {
7373+ this.enc = enc;
7474+ this.pos = pos;
7575+ }
7676+ if (typeof this.pos != 'number')
7777+ throw new Error('"pos" must be a numeric value');
7878+ if (typeof this.enc == 'string')
7979+ this.getRaw = pos => this.enc.charCodeAt(pos);
8080+ else if (typeof this.enc[0] == 'number')
8181+ this.getRaw = pos => this.enc[pos];
8282+ else
8383+ throw new Error('"enc" must be a numeric array or a string');
6584 }
6666- return s;
6767-};
6868-var b64Safe = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
6969-Stream.prototype.b64Dump = function (start, end) {
7070- var extra = (end - start) % 3,
7171- s = '',
7272- i, c;
7373- for (i = start; i + 2 < end; i += 3) {
7474- c = this.get(i) << 16 | this.get(i + 1) << 8 | this.get(i + 2);
7575- s += b64Safe.charAt(c >> 18 & 0x3F);
7676- s += b64Safe.charAt(c >> 12 & 0x3F);
7777- s += b64Safe.charAt(c >> 6 & 0x3F);
7878- s += b64Safe.charAt(c & 0x3F);
8585+ /** Get the byte at current position (and increment it) or at a specified position (and avoid moving current position).
8686+ * @param {?number} pos read position if specified, else current position (and increment it) */
8787+ get(pos) {
8888+ if (pos === undefined)
8989+ pos = this.pos++;
9090+ if (pos >= this.enc.length)
9191+ throw new Error('Requesting byte offset ' + pos + ' on a stream of length ' + this.enc.length);
9292+ return this.getRaw(pos);
7993 }
8080- if (extra > 0) {
8181- c = this.get(i) << 16;
8282- if (extra > 1) c |= this.get(i + 1) << 8;
8383- s += b64Safe.charAt(c >> 18 & 0x3F);
8484- s += b64Safe.charAt(c >> 12 & 0x3F);
8585- if (extra == 2) s += b64Safe.charAt(c >> 6 & 0x3F);
9494+ /** Convert a single byte to an hexadcimal string (of length 2).
9595+ * @param {number} b */
9696+ static hexByte(b) {
9797+ return hexDigits.charAt((b >> 4) & 0xF) + hexDigits.charAt(b & 0xF);
8698 }
8787- return s;
8888-};
8989-Stream.prototype.isASCII = function (start, end) {
9090- for (var i = start; i < end; ++i) {
9191- var c = this.get(i);
9292- if (c < 32 || c > 176)
9393- return false;
9999+ /** Hexadecimal dump of a specified region of the stream.
100100+ * @param {number} start starting position (included)
101101+ * @param {number} end ending position (excluded)
102102+ * @param {string} type 'raw', 'byte' or 'dump' (default) */
103103+ hexDump(start, end, type = 'dump') {
104104+ let s = '';
105105+ for (let i = start; i < end; ++i) {
106106+ if (type == 'byte' && i > start)
107107+ s += ' ';
108108+ s += Stream.hexByte(this.get(i));
109109+ if (type == 'dump')
110110+ switch (i & 0xF) {
111111+ case 0x7: s += ' '; break;
112112+ case 0xF: s += '\n'; break;
113113+ default: s += ' ';
114114+ }
115115+ }
116116+ return s;
94117 }
9595- return true;
9696-};
9797-Stream.prototype.parseStringISO = function (start, end) {
9898- var s = "";
9999- for (var i = start; i < end; ++i)
100100- s += String.fromCharCode(this.get(i));
101101- return s;
102102-};
103103-Stream.prototype.parseStringUTF = function (start, end) {
104104- function ex(c) { // must be 10xxxxxx
105105- if ((c < 0x80) || (c >= 0xC0))
106106- throw new Error('Invalid UTF-8 continuation byte: ' + c);
107107- return (c & 0x3F);
118118+ /** Base64url dump of a specified region of the stream (according to RFC 4648 section 5).
119119+ * @param {number} start starting position (included)
120120+ * @param {number} end ending position (excluded)
121121+ * @param {string} type 'url' (default, section 5 without padding) or 'std' (section 4 with padding) */
122122+ b64Dump(start, end, type = 'url') {
123123+ const b64 = type === 'url' ? b64URL : b64Std;
124124+ let extra = (end - start) % 3,
125125+ s = '',
126126+ i, c;
127127+ for (i = start; i + 2 < end; i += 3) {
128128+ c = this.get(i) << 16 | this.get(i + 1) << 8 | this.get(i + 2);
129129+ s += b64.charAt(c >> 18 & 0x3F);
130130+ s += b64.charAt(c >> 12 & 0x3F);
131131+ s += b64.charAt(c >> 6 & 0x3F);
132132+ s += b64.charAt(c & 0x3F);
133133+ }
134134+ if (extra > 0) {
135135+ c = this.get(i) << 16;
136136+ if (extra > 1) c |= this.get(i + 1) << 8;
137137+ s += b64.charAt(c >> 18 & 0x3F);
138138+ s += b64.charAt(c >> 12 & 0x3F);
139139+ if (extra == 2) s += b64.charAt(c >> 6 & 0x3F);
140140+ if (b64 === b64Std) s += '==='.slice(0, 3 - extra);
141141+ }
142142+ return s;
108143 }
109109- function surrogate(cp) {
110110- if (cp < 0x10000)
111111- throw new Error('UTF-8 overlong encoding, codepoint encoded in 4 bytes: ' + cp);
112112- // we could use String.fromCodePoint(cp) but let's be nice to older browsers and use surrogate pairs
113113- cp -= 0x10000;
114114- return String.fromCharCode((cp >> 10) + 0xD800, (cp & 0x3FF) + 0xDC00);
144144+ isASCII(start, end) {
145145+ for (let i = start; i < end; ++i) {
146146+ let c = this.get(i);
147147+ if (c < 32 || c > 176)
148148+ return false;
149149+ }
150150+ return true;
115151 }
116116- var s = "";
117117- for (var i = start; i < end; ) {
118118- var c = this.get(i++);
119119- if (c < 0x80) // 0xxxxxxx (7 bit)
120120- s += String.fromCharCode(c);
121121- else if (c < 0xC0)
122122- throw new Error('Invalid UTF-8 starting byte: ' + c);
123123- else if (c < 0xE0) // 110xxxxx 10xxxxxx (11 bit)
124124- s += String.fromCharCode(((c & 0x1F) << 6) | ex(this.get(i++)));
125125- else if (c < 0xF0) // 1110xxxx 10xxxxxx 10xxxxxx (16 bit)
126126- s += String.fromCharCode(((c & 0x0F) << 12) | (ex(this.get(i++)) << 6) | ex(this.get(i++)));
127127- else if (c < 0xF8) // 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx (21 bit)
128128- s += surrogate(((c & 0x07) << 18) | (ex(this.get(i++)) << 12) | (ex(this.get(i++)) << 6) | ex(this.get(i++)));
129129- else
130130- throw new Error('Invalid UTF-8 starting byte (since 2003 it is restricted to 4 bytes): ' + c);
152152+ parseStringISO(start, end, maxLength) {
153153+ let s = '';
154154+ for (let i = start; i < end; ++i)
155155+ s += String.fromCharCode(this.get(i));
156156+ return { size: s.length, str: stringCut(s, maxLength) };
131157 }
132132- return s;
133133-};
134134-Stream.prototype.parseStringBMP = function (start, end) {
135135- var str = "", hi, lo;
136136- for (var i = start; i < end; ) {
137137- hi = this.get(i++);
138138- lo = this.get(i++);
139139- str += String.fromCharCode((hi << 8) | lo);
158158+ parseStringT61(start, end, maxLength) {
159159+ // warning: this code is not very well tested so far
160160+ function merge(c, d) {
161161+ let t = tableT61[c - 0xC0];
162162+ let i = t[0].indexOf(String.fromCharCode(d));
163163+ return (i < 0) ? '\0' : t[1].charAt(i);
164164+ }
165165+ let s = '', c;
166166+ for (let i = start; i < end; ++i) {
167167+ c = this.get(i);
168168+ if (c >= 0xA4 && c <= 0xBF)
169169+ s += '$ยฅ#ยงยค\0\0ยซ\0\0\0\0ยฐยฑยฒยณรยตยถยทรท\0\0ยปยผยฝยพยฟ'.charAt(c - 0xA4);
170170+ else if (c >= 0xE0 && c <= 0xFF)
171171+ s += 'โฆรรยชฤฆ\0ฤฒฤฟลรลยบรลฆลลฤธรฆฤรฐฤงฤฑฤณลลรธลรรพลงล\0'.charAt(c - 0xE0);
172172+ else if (c >= 0xC0 && c <= 0xCF)
173173+ s += merge(c, this.get(++i));
174174+ else // using ISO 8859-1 for characters undefined (or equal) in T.61
175175+ s += String.fromCharCode(c);
176176+ }
177177+ return { size: s.length, str: stringCut(s, maxLength) };
140178 }
141141- return str;
142142-};
143143-Stream.prototype.parseTime = function (start, end, shortYear) {
144144- var s = this.parseStringISO(start, end),
145145- m = (shortYear ? reTimeS : reTimeL).exec(s);
146146- if (!m)
147147- return "Unrecognized time: " + s;
148148- if (shortYear) {
149149- // to avoid querying the timer, use the fixed range [1970, 2069]
150150- // it will conform with ITU X.400 [-10, +40] sliding window until 2030
151151- m[1] = +m[1];
152152- m[1] += (m[1] < 70) ? 2000 : 1900;
179179+ parseStringUTF(start, end, maxLength) {
180180+ function ex(c) { // must be 10xxxxxx
181181+ if ((c < 0x80) || (c >= 0xC0))
182182+ throw new Error('Invalid UTF-8 continuation byte: ' + c);
183183+ return (c & 0x3F);
184184+ }
185185+ function surrogate(cp) {
186186+ if (cp < 0x10000)
187187+ throw new Error('UTF-8 overlong encoding, codepoint encoded in 4 bytes: ' + cp);
188188+ // we could use String.fromCodePoint(cp) but let's be nice to older browsers and use surrogate pairs
189189+ cp -= 0x10000;
190190+ return String.fromCharCode((cp >> 10) + 0xD800, (cp & 0x3FF) + 0xDC00);
191191+ }
192192+ let s = '';
193193+ for (let i = start; i < end; ) {
194194+ let c = this.get(i++);
195195+ if (c < 0x80) // 0xxxxxxx (7 bit)
196196+ s += String.fromCharCode(c);
197197+ else if (c < 0xC0)
198198+ throw new Error('Invalid UTF-8 starting byte: ' + c);
199199+ else if (c < 0xE0) // 110xxxxx 10xxxxxx (11 bit)
200200+ s += String.fromCharCode(((c & 0x1F) << 6) | ex(this.get(i++)));
201201+ else if (c < 0xF0) // 1110xxxx 10xxxxxx 10xxxxxx (16 bit)
202202+ s += String.fromCharCode(((c & 0x0F) << 12) | (ex(this.get(i++)) << 6) | ex(this.get(i++)));
203203+ else if (c < 0xF8) // 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx (21 bit)
204204+ s += surrogate(((c & 0x07) << 18) | (ex(this.get(i++)) << 12) | (ex(this.get(i++)) << 6) | ex(this.get(i++)));
205205+ else
206206+ throw new Error('Invalid UTF-8 starting byte (since 2003 it is restricted to 4 bytes): ' + c);
207207+ }
208208+ return { size: s.length, str: stringCut(s, maxLength) };
153209 }
154154- s = m[1] + "-" + m[2] + "-" + m[3] + " " + m[4];
155155- if (m[5]) {
156156- s += ":" + m[5];
157157- if (m[6]) {
158158- s += ":" + m[6];
159159- if (m[7])
160160- s += "." + m[7];
210210+ parseStringBMP(start, end, maxLength) {
211211+ let s = '', hi, lo;
212212+ for (let i = start; i < end; ) {
213213+ hi = this.get(i++);
214214+ lo = this.get(i++);
215215+ s += String.fromCharCode((hi << 8) | lo);
161216 }
217217+ return { size: s.length, str: stringCut(s, maxLength) };
162218 }
163163- if (m[8]) {
164164- s += " UTC";
165165- if (m[8] != 'Z') {
166166- s += m[8];
219219+ parseTime(start, end, shortYear) {
220220+ let s = this.parseStringISO(start, end).str,
221221+ m = (shortYear ? reTimeS : reTimeL).exec(s);
222222+ if (!m)
223223+ throw new Error('Unrecognized time: ' + s);
224224+ if (shortYear) {
225225+ // to avoid querying the timer, use the fixed range [1970, 2069]
226226+ // it will conform with ITU X.400 [-10, +40] sliding window until 2030
227227+ m[1] = +m[1];
228228+ m[1] += (m[1] < 70) ? 2000 : 1900;
229229+ }
230230+ s = m[1] + '-' + m[2] + '-' + m[3] + ' ' + m[4];
231231+ if (m[5]) {
232232+ s += ':' + m[5];
233233+ if (m[6]) {
234234+ s += ':' + m[6];
235235+ if (m[7])
236236+ s += '.' + m[7];
237237+ }
238238+ }
239239+ if (m[8]) {
240240+ s += ' UTC';
167241 if (m[9])
168168- s += ":" + m[9];
242242+ s += m[9] + ':' + (m[10] || '00');
169243 }
244244+ return s;
170245 }
171171- return s;
172172-};
173173-Stream.prototype.parseInteger = function (start, end) {
174174- var v = this.get(start),
175175- neg = (v > 127),
176176- pad = neg ? 255 : 0,
177177- len,
178178- s = '';
179179- // skip unuseful bits (not allowed in DER)
180180- while (v == pad && ++start < end)
181181- v = this.get(start);
182182- len = end - start;
183183- if (len === 0)
184184- return neg ? '-1' : '0';
185185- // show bit length of huge integers
186186- if (len > 4) {
187187- s = v;
188188- len <<= 3;
189189- while (((s ^ pad) & 0x80) == 0) {
190190- s <<= 1;
191191- --len;
246246+ parseInteger(start, end) {
247247+ let v = this.get(start),
248248+ neg = (v > 127),
249249+ pad = neg ? 255 : 0,
250250+ len,
251251+ s = '';
252252+ // skip unuseful bits (not allowed in DER)
253253+ while (v == pad && ++start < end)
254254+ v = this.get(start);
255255+ len = end - start;
256256+ if (len === 0)
257257+ return neg ? '-1' : '0';
258258+ // show bit length of huge integers
259259+ if (len > 4) {
260260+ s = v;
261261+ len <<= 3;
262262+ while (((s ^ pad) & 0x80) == 0) {
263263+ s <<= 1;
264264+ --len;
265265+ }
266266+ s = '(' + len + ' bit)\n';
192267 }
193193- s = "(" + len + " bit)\n";
268268+ // decode the integer
269269+ if (neg) v = v - 256;
270270+ let n = new Int10(v);
271271+ for (let i = start + 1; i < end; ++i)
272272+ n.mulAdd(256, this.get(i));
273273+ return s + n.toString();
194274 }
195195- // decode the integer
196196- if (neg) v = v - 256;
197197- var n = new Int10(v);
198198- for (var i = start + 1; i < end; ++i)
199199- n.mulAdd(256, this.get(i));
200200- return s + n.toString();
201201-};
202202-Stream.prototype.parseBitString = function (start, end, maxLength) {
203203- var unusedBits = this.get(start);
204204- if (unusedBits > 7)
205205- throw 'Invalid BitString with unusedBits=' + unusedBits;
206206- var lenBit = ((end - start - 1) << 3) - unusedBits,
207207- s = "";
208208- for (var i = start + 1; i < end; ++i) {
209209- var b = this.get(i),
210210- skip = (i == end - 1) ? unusedBits : 0;
211211- for (var j = 7; j >= skip; --j)
212212- s += (b >> j) & 1 ? "1" : "0";
213213- if (s.length > maxLength)
214214- s = stringCut(s, maxLength);
275275+ parseBitString(start, end, maxLength) {
276276+ let unusedBits = this.get(start);
277277+ if (unusedBits > 7)
278278+ throw new Error('Invalid BitString with unusedBits=' + unusedBits);
279279+ let lenBit = ((end - start - 1) << 3) - unusedBits,
280280+ s = '';
281281+ for (let i = start + 1; i < end; ++i) {
282282+ let b = this.get(i),
283283+ skip = (i == end - 1) ? unusedBits : 0;
284284+ for (let j = 7; j >= skip; --j)
285285+ s += (b >> j) & 1 ? '1' : '0';
286286+ if (s.length > maxLength)
287287+ s = stringCut(s, maxLength);
288288+ }
289289+ return { size: lenBit, str: s };
215290 }
216216- return { size: lenBit, str: s };
217217-};
218218-Stream.prototype.parseOctetString = function (start, end, maxLength) {
219219- var len = end - start,
220220- s;
221221- try {
222222- s = this.parseStringUTF(start, end);
223223- var v;
224224- for (i = 0; i < s.length; ++i) {
225225- v = s.charCodeAt(i);
226226- if (v < 32 && v != 9 && v != 10 && v != 13) // [\t\r\n] are (kinda) printable
227227- throw new Error('Unprintable character at index ' + i + ' (code ' + s.charCodeAt(i) + ")");
291291+ parseOctetString(start, end, maxLength) {
292292+ let len = end - start,
293293+ s;
294294+ try {
295295+ s = this.parseStringUTF(start, end, maxLength);
296296+ checkPrintable(s.str);
297297+ return { size: end - start, str: s.str };
298298+ } catch (e) {
299299+ // ignore
228300 }
301301+ maxLength /= 2; // we work in bytes
302302+ if (len > maxLength)
303303+ end = start + maxLength;
304304+ s = '';
305305+ for (let i = start; i < end; ++i)
306306+ s += Stream.hexByte(this.get(i));
307307+ if (len > maxLength)
308308+ s += ellipsis;
229309 return { size: len, str: s };
230230- } catch (e) {
231231- // ignore
232310 }
233233- maxLength /= 2; // we work in bytes
234234- if (len > maxLength)
235235- end = start + maxLength;
236236- s = '';
237237- for (var i = start; i < end; ++i)
238238- s += this.hexByte(this.get(i));
239239- if (len > maxLength)
240240- s += ellipsis;
241241- return { size: len, str: s };
242242-};
243243-Stream.prototype.parseOID = function (start, end, maxLength) {
244244- var s = '',
245245- n = new Int10(),
246246- bits = 0;
247247- for (var i = start; i < end; ++i) {
248248- var v = this.get(i);
249249- n.mulAdd(128, v & 0x7F);
250250- bits += 7;
251251- if (!(v & 0x80)) { // finished
252252- if (s === '') {
253253- n = n.simplify();
254254- if (n instanceof Int10) {
255255- n.sub(80);
256256- s = "2." + n.toString();
257257- } else {
258258- var m = n < 80 ? n < 40 ? 0 : 1 : 2;
259259- s = m + "." + (n - m * 40);
260260- }
261261- } else
262262- s += "." + n.toString();
263263- if (s.length > maxLength)
264264- return stringCut(s, maxLength);
265265- n = new Int10();
311311+ parseOID(start, end, maxLength, isRelative) {
312312+ let s = '',
313313+ n = new Int10(),
266314 bits = 0;
315315+ for (let i = start; i < end; ++i) {
316316+ let v = this.get(i);
317317+ n.mulAdd(128, v & 0x7F);
318318+ bits += 7;
319319+ if (!(v & 0x80)) { // finished
320320+ if (s === '') {
321321+ n = n.simplify();
322322+ if (isRelative) {
323323+ s = (n instanceof Int10) ? n.toString() : '' + n;
324324+ } else if (n instanceof Int10) {
325325+ n.sub(80);
326326+ s = '2.' + n.toString();
327327+ } else {
328328+ let m = n < 80 ? n < 40 ? 0 : 1 : 2;
329329+ s = m + '.' + (n - m * 40);
330330+ }
331331+ } else
332332+ s += '.' + n.toString();
333333+ if (s.length > maxLength)
334334+ return stringCut(s, maxLength);
335335+ n = new Int10();
336336+ bits = 0;
337337+ }
267338 }
339339+ if (bits > 0)
340340+ s += '.incomplete';
341341+ if (typeof oids === 'object' && !isRelative) {
342342+ let oid = oids[s];
343343+ if (oid) {
344344+ if (oid.d) s += '\n' + oid.d;
345345+ if (oid.c) s += '\n' + oid.c;
346346+ if (oid.w) s += '\n(warning!)';
347347+ }
348348+ }
349349+ return s;
268350 }
269269- if (bits > 0)
270270- s += ".incomplete";
271271- if (typeof oids === 'object') {
272272- var oid = oids[s];
273273- if (oid) {
274274- if (oid.d) s += "\n" + oid.d;
275275- if (oid.c) s += "\n" + oid.c;
276276- if (oid.w) s += "\n(warning!)";
277277- }
351351+ parseRelativeOID(start, end, maxLength) {
352352+ return this.parseOID(start, end, maxLength, true);
278353 }
279279- return s;
280280-};
281281-282282-function ASN1(stream, header, length, tag, tagLen, sub) {
283283- if (!(tag instanceof ASN1Tag)) throw 'Invalid tag value.';
284284- this.stream = stream;
285285- this.header = header;
286286- this.length = length;
287287- this.tag = tag;
288288- this.tagLen = tagLen;
289289- this.sub = sub;
290354}
291291-ASN1.prototype.typeName = function () {
292292- switch (this.tag.tagClass) {
293293- case 0: // universal
294294- switch (this.tag.tagNumber) {
295295- case 0x00: return "EOC";
296296- case 0x01: return "BOOLEAN";
297297- case 0x02: return "INTEGER";
298298- case 0x03: return "BIT_STRING";
299299- case 0x04: return "OCTET_STRING";
300300- case 0x05: return "NULL";
301301- case 0x06: return "OBJECT_IDENTIFIER";
302302- case 0x07: return "ObjectDescriptor";
303303- case 0x08: return "EXTERNAL";
304304- case 0x09: return "REAL";
305305- case 0x0A: return "ENUMERATED";
306306- case 0x0B: return "EMBEDDED_PDV";
307307- case 0x0C: return "UTF8String";
308308- case 0x10: return "SEQUENCE";
309309- case 0x11: return "SET";
310310- case 0x12: return "NumericString";
311311- case 0x13: return "PrintableString"; // ASCII subset
312312- case 0x14: return "TeletexString"; // aka T61String
313313- case 0x15: return "VideotexString";
314314- case 0x16: return "IA5String"; // ASCII
315315- case 0x17: return "UTCTime";
316316- case 0x18: return "GeneralizedTime";
317317- case 0x19: return "GraphicString";
318318- case 0x1A: return "VisibleString"; // ASCII subset
319319- case 0x1B: return "GeneralString";
320320- case 0x1C: return "UniversalString";
321321- case 0x1E: return "BMPString";
322322- }
323323- return "Universal_" + this.tag.tagNumber.toString();
324324- case 1: return "Application_" + this.tag.tagNumber.toString();
325325- case 2: return "[" + this.tag.tagNumber.toString() + "]"; // Context
326326- case 3: return "Private_" + this.tag.tagNumber.toString();
327327- }
328328-};
355355+329356function recurse(el, parser, maxLength) {
330330- var avoidRecurse = true;
357357+ let avoidRecurse = true;
331358 if (el.tag.tagConstructed && el.sub) {
332359 avoidRecurse = false;
333360 el.sub.forEach(function (e1) {
···337364 }
338365 if (avoidRecurse)
339366 return el.stream[parser](el.posContent(), el.posContent() + Math.abs(el.length), maxLength);
340340- var d = { size: 0, str: '' };
367367+ let d = { size: 0, str: '' };
341368 el.sub.forEach(function (el) {
342342- var d1 = recurse(el, parser, maxLength - d.str.length);
369369+ let d1 = recurse(el, parser, maxLength - d.str.length);
343370 d.size += d1.size;
344371 d.str += d1.str;
345372 });
346373 return d;
347374}
348348-/** A string preview of the content (intended for humans). */
349349-ASN1.prototype.content = function (maxLength) {
350350- if (this.tag === undefined)
375375+376376+class ASN1Tag {
377377+ constructor(stream) {
378378+ let buf = stream.get();
379379+ this.tagClass = buf >> 6;
380380+ this.tagConstructed = ((buf & 0x20) !== 0);
381381+ this.tagNumber = buf & 0x1F;
382382+ if (this.tagNumber == 0x1F) { // long tag
383383+ let n = new Int10();
384384+ do {
385385+ buf = stream.get();
386386+ n.mulAdd(128, buf & 0x7F);
387387+ } while (buf & 0x80);
388388+ this.tagNumber = n.simplify();
389389+ }
390390+ }
391391+ isUniversal() {
392392+ return this.tagClass === 0x00;
393393+ }
394394+ isEOC() {
395395+ return this.tagClass === 0x00 && this.tagNumber === 0x00;
396396+ }
397397+}
398398+399399+export class ASN1 {
400400+ constructor(stream, header, length, tag, tagLen, sub) {
401401+ if (!(tag instanceof ASN1Tag)) throw new Error('Invalid tag value.');
402402+ this.stream = stream;
403403+ this.header = header;
404404+ this.length = length;
405405+ this.tag = tag;
406406+ this.tagLen = tagLen;
407407+ this.sub = sub;
408408+ }
409409+ typeName() {
410410+ switch (this.tag.tagClass) {
411411+ case 0: // universal
412412+ switch (this.tag.tagNumber) {
413413+ case 0x00: return 'EOC';
414414+ case 0x01: return 'BOOLEAN';
415415+ case 0x02: return 'INTEGER';
416416+ case 0x03: return 'BIT_STRING';
417417+ case 0x04: return 'OCTET_STRING';
418418+ case 0x05: return 'NULL';
419419+ case 0x06: return 'OBJECT_IDENTIFIER';
420420+ case 0x07: return 'ObjectDescriptor';
421421+ case 0x08: return 'EXTERNAL';
422422+ case 0x09: return 'REAL';
423423+ case 0x0A: return 'ENUMERATED';
424424+ case 0x0B: return 'EMBEDDED_PDV';
425425+ case 0x0C: return 'UTF8String';
426426+ case 0x0D: return 'RELATIVE_OID';
427427+ case 0x10: return 'SEQUENCE';
428428+ case 0x11: return 'SET';
429429+ case 0x12: return 'NumericString';
430430+ case 0x13: return 'PrintableString'; // ASCII subset
431431+ case 0x14: return 'TeletexString'; // aka T61String
432432+ case 0x15: return 'VideotexString';
433433+ case 0x16: return 'IA5String'; // ASCII
434434+ case 0x17: return 'UTCTime';
435435+ case 0x18: return 'GeneralizedTime';
436436+ case 0x19: return 'GraphicString';
437437+ case 0x1A: return 'VisibleString'; // ASCII subset
438438+ case 0x1B: return 'GeneralString';
439439+ case 0x1C: return 'UniversalString';
440440+ case 0x1E: return 'BMPString';
441441+ }
442442+ return 'Universal_' + this.tag.tagNumber.toString();
443443+ case 1: return 'Application_' + this.tag.tagNumber.toString();
444444+ case 2: return '[' + this.tag.tagNumber.toString() + ']'; // Context
445445+ case 3: return 'Private_' + this.tag.tagNumber.toString();
446446+ }
447447+ }
448448+ /** A string preview of the content (intended for humans). */
449449+ content(maxLength) {
450450+ if (this.tag === undefined)
451451+ return null;
452452+ if (maxLength === undefined)
453453+ maxLength = Infinity;
454454+ let content = this.posContent(),
455455+ len = Math.abs(this.length);
456456+ if (!this.tag.isUniversal()) {
457457+ if (this.sub !== null)
458458+ return '(' + this.sub.length + ' elem)';
459459+ let d1 = this.stream.parseOctetString(content, content + len, maxLength);
460460+ return '(' + d1.size + ' byte)\n' + d1.str;
461461+ }
462462+ switch (this.tag.tagNumber) {
463463+ case 0x01: // BOOLEAN
464464+ return (this.stream.get(content) === 0) ? 'false' : 'true';
465465+ case 0x02: // INTEGER
466466+ return this.stream.parseInteger(content, content + len);
467467+ case 0x03: { // BIT_STRING
468468+ let d = recurse(this, 'parseBitString', maxLength);
469469+ return '(' + d.size + ' bit)\n' + d.str;
470470+ }
471471+ case 0x04: { // OCTET_STRING
472472+ let d = recurse(this, 'parseOctetString', maxLength);
473473+ return '(' + d.size + ' byte)\n' + d.str;
474474+ }
475475+ //case 0x05: // NULL
476476+ case 0x06: // OBJECT_IDENTIFIER
477477+ return this.stream.parseOID(content, content + len, maxLength);
478478+ //case 0x07: // ObjectDescriptor
479479+ //case 0x08: // EXTERNAL
480480+ //case 0x09: // REAL
481481+ case 0x0A: // ENUMERATED
482482+ return this.stream.parseInteger(content, content + len);
483483+ //case 0x0B: // EMBEDDED_PDV
484484+ case 0x0D: // RELATIVE-OID
485485+ return this.stream.parseRelativeOID(content, content + len, maxLength);
486486+ case 0x10: // SEQUENCE
487487+ case 0x11: // SET
488488+ if (this.sub !== null)
489489+ return '(' + this.sub.length + ' elem)';
490490+ else
491491+ return '(no elem)';
492492+ case 0x0C: // UTF8String
493493+ return recurse(this, 'parseStringUTF', maxLength).str;
494494+ case 0x14: // TeletexString
495495+ return recurse(this, 'parseStringT61', maxLength).str;
496496+ case 0x12: // NumericString
497497+ case 0x13: // PrintableString
498498+ case 0x15: // VideotexString
499499+ case 0x16: // IA5String
500500+ case 0x1A: // VisibleString
501501+ case 0x1B: // GeneralString
502502+ //case 0x19: // GraphicString
503503+ //case 0x1C: // UniversalString
504504+ return recurse(this, 'parseStringISO', maxLength).str;
505505+ case 0x1E: // BMPString
506506+ return recurse(this, 'parseStringBMP', maxLength).str;
507507+ case 0x17: // UTCTime
508508+ case 0x18: // GeneralizedTime
509509+ return this.stream.parseTime(content, content + len, (this.tag.tagNumber == 0x17));
510510+ }
351511 return null;
352352- if (maxLength === undefined)
353353- maxLength = Infinity;
354354- var content = this.posContent(),
355355- len = Math.abs(this.length);
356356- if (!this.tag.isUniversal()) {
357357- if (this.sub !== null)
358358- return "(" + this.sub.length + " elem)";
359359- var d1 = this.stream.parseOctetString(content, content + len, maxLength);
360360- return "(" + d1.size + " byte)\n" + d1.str;
512512+ }
513513+ toString() {
514514+ return this.typeName() + '@' + this.stream.pos + '[header:' + this.header + ',length:' + this.length + ',sub:' + ((this.sub === null) ? 'null' : this.sub.length) + ']';
361515 }
362362- switch (this.tag.tagNumber) {
363363- case 0x01: // BOOLEAN
364364- return (this.stream.get(content) === 0) ? "false" : "true";
365365- case 0x02: // INTEGER
366366- return this.stream.parseInteger(content, content + len);
367367- case 0x03: // BIT_STRING
368368- var d = recurse(this, 'parseBitString', maxLength);
369369- return "(" + d.size + " bit)\n" + d.str;
370370- case 0x04: // OCTET_STRING
371371- d = recurse(this, 'parseOctetString', maxLength);
372372- return "(" + d.size + " byte)\n" + d.str;
373373- //case 0x05: // NULL
374374- case 0x06: // OBJECT_IDENTIFIER
375375- return this.stream.parseOID(content, content + len, maxLength);
376376- //case 0x07: // ObjectDescriptor
377377- //case 0x08: // EXTERNAL
378378- //case 0x09: // REAL
379379- case 0x0A: // ENUMERATED
380380- return this.stream.parseInteger(content, content + len);
381381- //case 0x0B: // EMBEDDED_PDV
382382- case 0x10: // SEQUENCE
383383- case 0x11: // SET
384384- if (this.sub !== null)
385385- return "(" + this.sub.length + " elem)";
386386- else
387387- return "(no elem)";
388388- case 0x0C: // UTF8String
389389- return stringCut(this.stream.parseStringUTF(content, content + len), maxLength);
390390- case 0x12: // NumericString
391391- case 0x13: // PrintableString
392392- case 0x14: // TeletexString
393393- case 0x15: // VideotexString
394394- case 0x16: // IA5String
395395- case 0x1A: // VisibleString
396396- case 0x1B: // GeneralString
397397- //case 0x19: // GraphicString
398398- //case 0x1C: // UniversalString
399399- return stringCut(this.stream.parseStringISO(content, content + len), maxLength);
400400- case 0x1E: // BMPString
401401- return stringCut(this.stream.parseStringBMP(content, content + len), maxLength);
402402- case 0x17: // UTCTime
403403- case 0x18: // GeneralizedTime
404404- return this.stream.parseTime(content, content + len, (this.tag.tagNumber == 0x17));
516516+ toPrettyString(indent) {
517517+ if (indent === undefined) indent = '';
518518+ let s = indent;
519519+ if (this.def) {
520520+ if (this.def.id)
521521+ s += this.def.id + ' ';
522522+ if (this.def.name && this.def.name != this.typeName().replace(/_/g, ' '))
523523+ s+= this.def.name + ' ';
524524+ if (this.def.mismatch)
525525+ s += '[?] ';
526526+ }
527527+ s += this.typeName() + ' @' + this.stream.pos;
528528+ if (this.length >= 0)
529529+ s += '+';
530530+ s += this.length;
531531+ if (this.tag.tagConstructed)
532532+ s += ' (constructed)';
533533+ else if ((this.tag.isUniversal() && ((this.tag.tagNumber == 0x03) || (this.tag.tagNumber == 0x04))) && (this.sub !== null))
534534+ s += ' (encapsulates)';
535535+ let content = this.content();
536536+ if (content)
537537+ s += ': ' + content.replace(/\n/g, '|');
538538+ s += '\n';
539539+ if (this.sub !== null) {
540540+ indent += ' ';
541541+ for (let i = 0, max = this.sub.length; i < max; ++i)
542542+ s += this.sub[i].toPrettyString(indent);
543543+ }
544544+ return s;
545545+ }
546546+ posStart() {
547547+ return this.stream.pos;
548548+ }
549549+ posContent() {
550550+ return this.stream.pos + this.header;
551551+ }
552552+ posEnd() {
553553+ return this.stream.pos + this.header + Math.abs(this.length);
554554+ }
555555+ /** Position of the length. */
556556+ posLen() {
557557+ return this.stream.pos + this.tagLen;
558558+ }
559559+ /** Hexadecimal dump of the node.
560560+ * @param type 'raw', 'byte' or 'dump' */
561561+ toHexString(type = 'raw') {
562562+ return this.stream.hexDump(this.posStart(), this.posEnd(), type);
405563 }
406406- return null;
407407-};
408408-ASN1.prototype.toString = function () {
409409- return this.typeName() + "@" + this.stream.pos + "[header:" + this.header + ",length:" + this.length + ",sub:" + ((this.sub === null) ? 'null' : this.sub.length) + "]";
410410-};
411411-ASN1.prototype.toPrettyString = function (indent) {
412412- if (indent === undefined) indent = '';
413413- var s = indent + this.typeName() + " @" + this.stream.pos;
414414- if (this.length >= 0)
415415- s += "+";
416416- s += this.length;
417417- if (this.tag.tagConstructed)
418418- s += " (constructed)";
419419- else if ((this.tag.isUniversal() && ((this.tag.tagNumber == 0x03) || (this.tag.tagNumber == 0x04))) && (this.sub !== null))
420420- s += " (encapsulates)";
421421- var content = this.content();
422422- if (content)
423423- s += ": " + content.replace(/\n/g, '|');
424424- s += "\n";
425425- if (this.sub !== null) {
426426- indent += ' ';
427427- for (var i = 0, max = this.sub.length; i < max; ++i)
428428- s += this.sub[i].toPrettyString(indent);
564564+ /** Base64url dump of the node (according to RFC 4648 section 5).
565565+ * @param {string} type 'url' (default, section 5 without padding) or 'std' (section 4 with padding)
566566+ */
567567+ toB64String(type = 'url') {
568568+ return this.stream.b64Dump(this.posStart(), this.posEnd(), type);
429569 }
430430- return s;
431431-};
432432-ASN1.prototype.posStart = function () {
433433- return this.stream.pos;
434434-};
435435-ASN1.prototype.posContent = function () {
436436- return this.stream.pos + this.header;
437437-};
438438-ASN1.prototype.posEnd = function () {
439439- return this.stream.pos + this.header + Math.abs(this.length);
440440-};
441441-/** Position of the length. */
442442-ASN1.prototype.posLen = function() {
443443- return this.stream.pos + this.tagLen;
444444-};
445445-ASN1.prototype.toHexString = function () {
446446- return this.stream.hexDump(this.posStart(), this.posEnd(), true);
447447-};
448448-ASN1.prototype.toB64String = function () {
449449- return this.stream.b64Dump(this.posStart(), this.posEnd());
450450-};
451451-ASN1.decodeLength = function (stream) {
452452- var buf = stream.get(),
453453- len = buf & 0x7F;
454454- if (len == buf) // first bit was 0, short form
455455- return len;
456456- if (len === 0) // long form with length 0 is a special case
457457- return null; // undefined length
458458- if (len > 6) // no reason to use Int10, as it would be a huge buffer anyways
459459- throw "Length over 48 bits not supported at position " + (stream.pos - 1);
460460- buf = 0;
461461- for (var i = 0; i < len; ++i)
462462- buf = (buf * 256) + stream.get();
463463- return buf;
464464-};
465465-function ASN1Tag(stream) {
466466- var buf = stream.get();
467467- this.tagClass = buf >> 6;
468468- this.tagConstructed = ((buf & 0x20) !== 0);
469469- this.tagNumber = buf & 0x1F;
470470- if (this.tagNumber == 0x1F) { // long tag
471471- var n = new Int10();
472472- do {
473473- buf = stream.get();
474474- n.mulAdd(128, buf & 0x7F);
475475- } while (buf & 0x80);
476476- this.tagNumber = n.simplify();
570570+ static decodeLength(stream) {
571571+ let buf = stream.get(),
572572+ len = buf & 0x7F;
573573+ if (len == buf) // first bit was 0, short form
574574+ return len;
575575+ if (len === 0) // long form with length 0 is a special case
576576+ return null; // undefined length
577577+ if (len > 6) // no reason to use Int10, as it would be a huge buffer anyways
578578+ throw new Error('Length over 48 bits not supported at position ' + (stream.pos - 1));
579579+ buf = 0;
580580+ for (let i = 0; i < len; ++i)
581581+ buf = (buf * 256) + stream.get();
582582+ return buf;
477583 }
478478-}
479479-ASN1Tag.prototype.isUniversal = function () {
480480- return this.tagClass === 0x00;
481481-};
482482-ASN1Tag.prototype.isEOC = function () {
483483- return this.tagClass === 0x00 && this.tagNumber === 0x00;
484484-};
485485-ASN1.decode = function (stream, offset) {
486486- if (!(stream instanceof Stream))
487487- stream = new Stream(stream, offset || 0);
488488- var streamStart = new Stream(stream),
489489- tag = new ASN1Tag(stream),
490490- tagLen = stream.pos - streamStart.pos,
491491- len = ASN1.decodeLength(stream),
492492- start = stream.pos,
493493- header = start - streamStart.pos,
494494- sub = null,
495495- getSub = function () {
496496- sub = [];
497497- if (len !== null) {
498498- // definite length
499499- var end = start + len;
500500- if (end > stream.enc.length)
501501- throw 'Container at offset ' + start + ' has a length of ' + len + ', which is past the end of the stream';
502502- while (stream.pos < end)
503503- sub[sub.length] = ASN1.decode(stream);
504504- if (stream.pos != end)
505505- throw 'Content size is not correct for container at offset ' + start;
506506- } else {
507507- // undefined length
508508- try {
509509- for (;;) {
510510- var s = ASN1.decode(stream);
511511- if (s.tag.isEOC())
512512- break;
513513- sub[sub.length] = s;
584584+ static decode(stream, offset, type = ASN1) {
585585+ if (!(type == ASN1 || type.prototype instanceof ASN1))
586586+ throw new Error('Must pass a class that extends ASN1');
587587+ if (!(stream instanceof Stream))
588588+ stream = new Stream(stream, offset || 0);
589589+ let streamStart = new Stream(stream),
590590+ tag = new ASN1Tag(stream),
591591+ tagLen = stream.pos - streamStart.pos,
592592+ len = ASN1.decodeLength(stream),
593593+ start = stream.pos,
594594+ header = start - streamStart.pos,
595595+ sub = null,
596596+ getSub = function () {
597597+ sub = [];
598598+ if (len !== null) {
599599+ // definite length
600600+ let end = start + len;
601601+ if (end > stream.enc.length)
602602+ throw new Error('Container at offset ' + start + ' has a length of ' + len + ', which is past the end of the stream');
603603+ while (stream.pos < end)
604604+ sub[sub.length] = type.decode(stream);
605605+ if (stream.pos != end)
606606+ throw new Error('Content size is not correct for container at offset ' + start);
607607+ } else {
608608+ // undefined length
609609+ try {
610610+ for (;;) {
611611+ let s = type.decode(stream);
612612+ if (s.tag.isEOC())
613613+ break;
614614+ sub[sub.length] = s;
615615+ }
616616+ len = start - stream.pos; // undefined lengths are represented as negative values
617617+ } catch (e) {
618618+ throw new Error('Exception while decoding undefined length content at offset ' + start + ': ' + e);
619619+ }
620620+ }
621621+ };
622622+ if (tag.tagConstructed) {
623623+ // must have valid content
624624+ getSub();
625625+ } else if (tag.isUniversal() && ((tag.tagNumber == 0x03) || (tag.tagNumber == 0x04))) {
626626+ // sometimes BitString and OctetString are used to encapsulate ASN.1
627627+ try {
628628+ if (tag.tagNumber == 0x03)
629629+ if (stream.get() != 0)
630630+ throw new Error('BIT STRINGs with unused bits cannot encapsulate.');
631631+ getSub();
632632+ for (let s of sub) {
633633+ if (s.tag.isEOC())
634634+ throw new Error('EOC is not supposed to be actual content.');
635635+ try {
636636+ s.content();
637637+ } catch (e) {
638638+ throw new Error('Unable to parse content: ' + e);
514639 }
515515- len = start - stream.pos; // undefined lengths are represented as negative values
516516- } catch (e) {
517517- throw 'Exception while decoding undefined length content at offset ' + start + ': ' + e;
518640 }
641641+ } catch (e) {
642642+ // but silently ignore when they don't
643643+ sub = null;
644644+ //DEBUG console.log('Could not decode structure at ' + start + ':', e);
519645 }
520520- };
521521- if (tag.tagConstructed) {
522522- // must have valid content
523523- getSub();
524524- } else if (tag.isUniversal() && ((tag.tagNumber == 0x03) || (tag.tagNumber == 0x04))) {
525525- // sometimes BitString and OctetString are used to encapsulate ASN.1
526526- try {
527527- if (tag.tagNumber == 0x03)
528528- if (stream.get() != 0)
529529- throw "BIT STRINGs with unused bits cannot encapsulate.";
530530- getSub();
531531- for (var i = 0; i < sub.length; ++i)
532532- if (sub[i].tag.isEOC())
533533- throw 'EOC is not supposed to be actual content.';
534534- } catch (e) {
535535- // but silently ignore when they don't
536536- sub = null;
537537- //DEBUG console.log('Could not decode structure at ' + start + ':', e);
646646+ }
647647+ if (sub === null) {
648648+ if (len === null)
649649+ throw new Error("We can't skip over an invalid tag with undefined length at offset " + start);
650650+ stream.pos = start + Math.abs(len);
538651 }
539539- }
540540- if (sub === null) {
541541- if (len === null)
542542- throw "We can't skip over an invalid tag with undefined length at offset " + start;
543543- stream.pos = start + Math.abs(len);
652652+ return new type(streamStart, header, len, tag, tagLen, sub);
544653 }
545545- return new ASN1(streamStart, header, len, tag, tagLen, sub);
546546-};
547654548548-return ASN1;
549549-550550-});
655655+}
+79-82
base64.js
···11// Base64 JavaScript decoder
22-// Copyright (c) 2008-2021 Lapo Luchini <lapo@lapo.it>
22+// Copyright (c) 2008 Lapo Luchini <lapo@lapo.it>
3344// Permission to use, copy, modify, and/or distribute this software for any
55// purpose with or without fee is hereby granted, provided that the above
66// copyright notice and this permission notice appear in all copies.
77-//
77+//
88// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
99// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1010// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
···1313// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1414// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15151616-(typeof define != 'undefined' ? define : function (factory) { 'use strict';
1717- if (typeof module == 'object') module.exports = factory();
1818- else window.base64 = factory();
1919-})(function () {
2020-"use strict";
1616+const
1717+ haveU8 = (typeof Uint8Array == 'function');
1818+1919+let decoder; // populated on first usage
21202222-var Base64 = {},
2323- decoder, // populated on first usage
2424- haveU8 = (typeof Uint8Array == 'function');
2121+export class Base64 {
25222626-Base64.decode = function (a) {
2727- var isString = (typeof a == 'string');
2828- var i;
2929- if (decoder === undefined) {
3030- var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",
3131- ignore = "= \f\n\r\t\u00A0\u2028\u2029";
3232- decoder = [];
3333- for (i = 0; i < 64; ++i)
3434- decoder[b64.charCodeAt(i)] = i;
3535- for (i = 0; i < ignore.length; ++i)
3636- decoder[ignore.charCodeAt(i)] = -1;
3737- // RFC 3548 URL & file safe encoding
3838- decoder['-'.charCodeAt(0)] = decoder['+'.charCodeAt(0)];
3939- decoder['_'.charCodeAt(0)] = decoder['/'.charCodeAt(0)];
4040- }
4141- var out = haveU8 ? new Uint8Array(a.length * 3 >> 2) : [];
4242- var bits = 0, char_count = 0, len = 0;
4343- for (i = 0; i < a.length; ++i) {
4444- var c = isString ? a.charCodeAt(i) : a[i];
4545- if (c == 61) // '='.charCodeAt(0)
2323+ static decode(a) {
2424+ let isString = (typeof a == 'string');
2525+ let i;
2626+ if (decoder === undefined) {
2727+ let b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
2828+ ignore = '= \f\n\r\t\u00A0\u2028\u2029';
2929+ decoder = [];
3030+ for (i = 0; i < 64; ++i)
3131+ decoder[b64.charCodeAt(i)] = i;
3232+ for (i = 0; i < ignore.length; ++i)
3333+ decoder[ignore.charCodeAt(i)] = -1;
3434+ // also support decoding Base64url (RFC 4648 section 5)
3535+ decoder['-'.charCodeAt(0)] = decoder['+'.charCodeAt(0)];
3636+ decoder['_'.charCodeAt(0)] = decoder['/'.charCodeAt(0)];
3737+ }
3838+ let out = haveU8 ? new Uint8Array(a.length * 3 >> 2) : [];
3939+ let bits = 0, char_count = 0, len = 0;
4040+ for (i = 0; i < a.length; ++i) {
4141+ let c = isString ? a.charCodeAt(i) : a[i];
4242+ if (c == 61) // '='.charCodeAt(0)
4343+ break;
4444+ c = decoder[c];
4545+ if (c == -1)
4646+ continue;
4747+ if (c === undefined)
4848+ throw 'Illegal character at offset ' + i;
4949+ bits |= c;
5050+ if (++char_count >= 4) {
5151+ out[len++] = (bits >> 16);
5252+ out[len++] = (bits >> 8) & 0xFF;
5353+ out[len++] = bits & 0xFF;
5454+ bits = 0;
5555+ char_count = 0;
5656+ } else {
5757+ bits <<= 6;
5858+ }
5959+ }
6060+ switch (char_count) {
6161+ case 1:
6262+ throw 'Base64 encoding incomplete: at least 2 bits missing';
6363+ case 2:
6464+ out[len++] = (bits >> 10);
4665 break;
4747- c = decoder[c];
4848- if (c == -1)
4949- continue;
5050- if (c === undefined)
5151- throw 'Illegal character at offset ' + i;
5252- bits |= c;
5353- if (++char_count >= 4) {
6666+ case 3:
5467 out[len++] = (bits >> 16);
5568 out[len++] = (bits >> 8) & 0xFF;
5656- out[len++] = bits & 0xFF;
5757- bits = 0;
5858- char_count = 0;
5959- } else {
6060- bits <<= 6;
6969+ break;
6170 }
6262- }
6363- switch (char_count) {
6464- case 1:
6565- throw "Base64 encoding incomplete: at least 2 bits missing";
6666- case 2:
6767- out[len++] = (bits >> 10);
6868- break;
6969- case 3:
7070- out[len++] = (bits >> 16);
7171- out[len++] = (bits >> 8) & 0xFF;
7272- break;
7171+ if (haveU8 && out.length > len) // in case it was originally longer because of ignored characters
7272+ out = out.subarray(0, len);
7373+ return out;
7374 }
7474- if (haveU8 && out.length > len) // in case it was originally longer because of ignored characters
7575- out = out.subarray(0, len);
7676- return out;
7777-};
78757979-Base64.pretty = function (str) {
8080- // fix padding
8181- if (str.length % 4 > 0)
8282- str = (str + '===').slice(0, str.length + str.length % 4);
8383- // convert RFC 3548 to standard Base64
8484- str = str.replace(/-/g, '+').replace(/_/g, '/');
8585- // 80 column width
8686- return str.replace(/(.{80})/g, '$1\n');
8787-};
7676+ static pretty(str) {
7777+ // fix padding
7878+ let pad = 4 - str.length % 4;
7979+ if (pad < 4)
8080+ str += '==='.slice(0, pad);
8181+ // convert Base64url (RFC 4648 section 5) to standard Base64 (RFC 4648 section 4)
8282+ str = str.replace(/-/g, '+').replace(/_/g, '/');
8383+ // 80 column width
8484+ return str.replace(/.{80}/g, '$&\n');
8585+ }
88868989-Base64.re = /-----BEGIN [^-]+-----([A-Za-z0-9+/=\s]+)-----END [^-]+-----|begin-base64[^\n]+\n([A-Za-z0-9+/=\s]+)====|^([A-Za-z0-9+/=\s]+)$/;
9090-Base64.unarmor = function (a) {
9191- var m = Base64.re.exec(a);
9292- if (m) {
9393- if (m[1])
9494- a = m[1];
9595- else if (m[2])
9696- a = m[2];
9797- else if (m[3])
9898- a = m[3];
9999- else
100100- throw "RegExp out of sync";
8787+ static unarmor(a) {
8888+ let m = Base64.re.exec(a);
8989+ if (m) {
9090+ if (m[1])
9191+ a = m[1];
9292+ else if (m[2])
9393+ a = m[2];
9494+ else if (m[3])
9595+ a = m[3];
9696+ else
9797+ throw 'RegExp out of sync';
9898+ }
9999+ return Base64.decode(a);
101100 }
102102- return Base64.decode(a);
103103-};
104101105105-return Base64;
102102+}
106103107107-});
104104+Base64.re = /-----BEGIN [^-]+-----([A-Za-z0-9+/=\s]+)-----END [^-]+-----|begin-base64[^\n]+\n([A-Za-z0-9+/=\s]+)====|^([A-Za-z0-9+/=\s]+)$/;
···11+const
22+ id = (elem) => document.getElementById(elem),
33+ contextMenu = id('contextmenu'),
44+ btnCopyHex = id('btnCopyHex'),
55+ btnCopyB64 = id('btnCopyB64'),
66+ btnCopyTree = id('btnCopyTree'),
77+ btnCopyValue = id('btnCopyValue');
88+99+export function bindContextMenu(node) {
1010+ const type = node.asn1.typeName();
1111+ const valueEnabled = type != 'SET' && type != 'SEQUENCE';
1212+ node.onclick = function (event) {
1313+ // do not show the menu in case of clicking the icon
1414+ if (event.srcElement.nodeName != 'SPAN') return;
1515+ contextMenu.style.left = event.pageX + 'px';
1616+ contextMenu.style.top = event.pageY + 'px';
1717+ contextMenu.style.visibility = 'visible';
1818+ contextMenu.node = this;
1919+ btnCopyValue.style.display = valueEnabled ? 'block' : 'none';
2020+ event.preventDefault();
2121+ event.stopPropagation();
2222+ };
2323+}
2424+2525+function close(event) {
2626+ contextMenu.style.visibility = 'hidden';
2727+ event.stopPropagation();
2828+}
2929+3030+contextMenu.onmouseleave = close;
3131+3232+btnCopyHex.onclick = function (event) {
3333+ navigator.clipboard.writeText(contextMenu.node.asn1.toHexString('byte'));
3434+ close(event);
3535+};
3636+3737+btnCopyB64.onclick = function (event) {
3838+ event.stopPropagation();
3939+ navigator.clipboard.writeText(contextMenu.node.asn1.toB64String());
4040+ close(event);
4141+};
4242+4343+btnCopyTree.onclick = function (event) {
4444+ event.stopPropagation();
4545+ navigator.clipboard.writeText(contextMenu.node.asn1.toPrettyString());
4646+ close(event);
4747+};
4848+4949+btnCopyValue.onclick = function (event) {
5050+ event.stopPropagation();
5151+ navigator.clipboard.writeText(contextMenu.node.asn1.content());
5252+ close(event);
5353+};
+141
defs.js
···11+// ASN.1 RFC definitions matcher
22+// Copyright (c) 2023 Lapo Luchini <lapo@lapo.it>
33+44+// Permission to use, copy, modify, and/or distribute this software for any
55+// purpose with or without fee is hereby granted, provided that the above
66+// copyright notice and this permission notice appear in all copies.
77+//
88+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
99+// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1010+// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1111+// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1212+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1313+// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1414+// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1515+1616+import { rfcdef } from './rfcdef.js';
1717+1818+function translate(def, tn, stats) {
1919+ if (def?.type == 'tag' && !def.explicit)
2020+ // def.type = def.content[0].type;
2121+ def = def.content[0].type;
2222+ if (def?.definedBy)
2323+ try {
2424+ // hope current OIDs contain the type name (will need to parse from RFC itself)
2525+ def = Defs.searchType(firstUpper(stats.defs[def.definedBy][1]));
2626+ } catch (e) { /*ignore*/ }
2727+ while (def?.type == 'defined' || def?.type?.type == 'defined') {
2828+ const name = def?.type?.type ? def.type.name : def.name;
2929+ def = Object.assign({}, def);
3030+ def.type = Defs.searchType(name).type;
3131+ }
3232+ if (def?.name == 'CHOICE' || def?.type?.name == 'CHOICE') {
3333+ for (let c of def.content ?? def.type.content) {
3434+ if (tn != c.type.name && tn != c.name)
3535+ c = translate(c);
3636+ if (tn == c.type.name || tn == c.name) {
3737+ def = Object.assign({}, def);
3838+ if (c.id) def.id = c.id;
3939+ def.type = c.type.name ? c.type : c;
4040+ break;
4141+ }
4242+ }
4343+ }
4444+ const id = def?.id;
4545+ if (id)
4646+ def = Object.assign({}, def, { id });
4747+ return def ?? { type: {} };
4848+}
4949+5050+function firstUpper(s) {
5151+ return s[0].toUpperCase() + s.slice(1);
5252+}
5353+5454+export class Defs {
5555+5656+ static moduleAndType(mod, name) {
5757+ return Object.assign({ module: { oid: mod.oid, name: mod.name, source: mod.source } }, mod.types[name]);
5858+ }
5959+6060+ static searchType(name) {
6161+ for (const mod of Object.values(rfcdef))
6262+ if (name in mod.types) {
6363+ // console.log(name + ' found in ' + r.name);
6464+ // return r.types[name];
6565+ return Defs.moduleAndType(mod, name);
6666+ }
6767+ throw new Error('Type not found: ' + name);
6868+ }
6969+7070+ static match(value, def, stats = { total: 0, recognized: 0, defs: {} }) {
7171+ value.def = {};
7272+ let tn = value.typeName().replaceAll('_', ' ');
7373+ def = translate(def, tn, stats);
7474+ ++stats.total;
7575+ if (def?.type) {
7676+ // if (def.id || def.name) ++stats.recognized;
7777+ if (tn == def.type.name || tn == def.name || def.name == 'ANY')
7878+ ++stats.recognized;
7979+ else if (def.name)
8080+ def = Object.assign({ mismatch: 1 }, def);
8181+ value.def = def;
8282+ }
8383+ if (value.sub !== null) {
8484+ if (def?.type?.type)
8585+ def = def.type;
8686+ let j = def?.content ? 0 : -1;
8787+ for (const subval of value.sub) {
8888+ let type;
8989+ if (j >= 0) {
9090+ if (def.typeOf)
9191+ type = def.content[0];
9292+ else {
9393+ let tn = subval.typeName().replaceAll('_', ' ');
9494+ for (;;) {
9595+ type = def.content[j++];
9696+ if (!type || typeof type != 'object') break;
9797+ if (type?.type?.type)
9898+ type = type.type;
9999+ if (type.type == 'defined') {
100100+ let t2 = translate(type, tn);
101101+ if (t2.type.name == tn) break; // exact match
102102+ if (t2.type.name == 'ANY') break; // good enough
103103+ }
104104+ if (type.name == tn) break; // exact match
105105+ if (type.name == 'ANY') break; // good enough
106106+ if (!('optional' in type || 'default' in type)) break;
107107+ }
108108+ if (type?.type == 'builtin' || type?.type == 'defined') {
109109+ let v = subval.content();
110110+ if (typeof v == 'string')
111111+ v = v.split(/\n/);
112112+ stats.defs[type.id] = v;
113113+ } else if (type?.definedBy && stats.defs?.[type.definedBy]?.[1]) { // hope current OIDs contain the type name (will need to parse from RFC itself)
114114+ try {
115115+ type = Defs.searchType(firstUpper(stats.defs[type.definedBy][1]));
116116+ } catch (e) { /*ignore*/ }
117117+ }
118118+ }
119119+ }
120120+ Defs.match(subval, type, stats);
121121+ }
122122+ }
123123+ return stats;
124124+ }
125125+126126+}
127127+128128+Defs.RFC = rfcdef;
129129+130130+Defs.commonTypes = [
131131+ [ 'X.509 certificate', '1.3.6.1.5.5.7.0.18', 'Certificate' ],
132132+ [ 'X.509 public key info', '1.3.6.1.5.5.7.0.18', 'SubjectPublicKeyInfo' ],
133133+ [ 'X.509 certificate revocation list', '1.3.6.1.5.5.7.0.18', 'CertificateList' ],
134134+ [ 'CMS / PKCS#7 envelope', '1.2.840.113549.1.9.16.0.14', 'ContentInfo' ],
135135+ [ 'PKCS#1 RSA private key', '1.2.840.113549.1.1.0.1', 'RSAPrivateKey' ],
136136+ [ 'PKCS#8 encrypted private key', '1.2.840.113549.1.8.1.1', 'EncryptedPrivateKeyInfo' ],
137137+ [ 'PKCS#8 private key', '1.2.840.113549.1.8.1.1', 'PrivateKeyInfo' ],
138138+ [ 'PKCS#10 certification request', '1.2.840.113549.1.10.1.1', 'CertificationRequest' ],
139139+ [ 'CMP PKI Message', '1.3.6.1.5.5.7.0.16', 'PKIMessage' ],
140140+ [ 'LDAP Message', '1.3.6.1.1.18', 'LDAPMessage' ],
141141+].map(arr => ({ description: arr[0], ...Defs.moduleAndType(rfcdef[arr[1]], arr[2]) }));
+204-176
dom.js
···11// ASN.1 JavaScript decoder
22-// Copyright (c) 2008-2021 Lapo Luchini <lapo@lapo.it>
22+// Copyright (c) 2008 Lapo Luchini <lapo@lapo.it>
3344// Permission to use, copy, modify, and/or distribute this software for any
55// purpose with or without fee is hereby granted, provided that the above
66// copyright notice and this permission notice appear in all copies.
77-//
77+//
88// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
99// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1010// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
···1313// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1414// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15151616-(typeof define != 'undefined' ? define : function (factory) { 'use strict';
1717- if (typeof module == 'object') factory(function (name) { return require(name); });
1818- else factory(function (name) { return window[name.substring(2)]; });
1919-})(function (require) {
2020-"use strict";
1616+import { ASN1 } from './asn1.js';
1717+import { oids } from './oids.js';
1818+import { bindContextMenu } from './context.js';
21192222-var ASN1 = require('./asn1'),
2323- oids = require('./oids'),
2020+const
2421 lineLength = 80,
2522 contentLength = 8 * lineLength,
2623 DOM = {
2727- ellipsis: "\u2026",
2828- tag: function (tagName, className) {
2929- var t = document.createElement(tagName);
3030- t.className = className;
2424+ ellipsis: '\u2026',
2525+ tag: function (tagName, className, text) {
2626+ let t = document.createElement(tagName);
2727+ if (className) t.className = className;
2828+ if (text) t.innerText = text;
3129 return t;
3230 },
3331 text: function (str) {
3432 return document.createTextNode(str);
3533 },
3634 space: function () {
3737- var t = document.createElement('span');
3838- t.className = 'spaces';
3939- t.innerHTML = ' ';
4040- return t;
3535+ return DOM.tag('span', 'spaces', ' ');
4136 },
4237 breakLines: function (str, length) {
4343- var lines = str.split(/\r?\n/),
3838+ let lines = str.split(/\r?\n/),
4439 o = '';
4545- for (var i = 0; i < lines.length; ++i) {
4646- var line = lines[i];
4747- if (i > 0) o += "\n";
4040+ for (let i = 0; i < lines.length; ++i) {
4141+ let line = lines[i];
4242+ if (i > 0) o += '\n';
4843 while (line.length > length) {
4944 o += line.substring(0, length);
5050- o += "\n";
4545+ o += '\n';
5146 line = line.substring(length);
5247 }
5348 o += line;
5449 }
5550 return o;
5656- }
5151+ },
5752 };
58535959-ASN1.prototype.toDOM = function (spaces) {
6060- spaces = spaces || '';
6161- var isOID = (typeof oids === 'object') && (this.tag.isUniversal() && (this.tag.tagNumber == 0x06));
6262- var node = DOM.tag("div", "node");
6363- node.asn1 = this;
6464- var head = DOM.tag("div", "head");
6565- head.innerHTML = "<span class='spaces'>" + spaces + "</span>" + this.typeName().replace(/_/g, " ");
6666- var content = this.content(contentLength);
6767- if (content !== null) {
6868- var preview = DOM.tag("span", "preview"),
6969- shortContent;
7070- if (isOID)
7171- content = content.split('\n', 1)[0];
7272- shortContent = (content.length > lineLength) ? content.substring(0, lineLength) + DOM.ellipsis : content;
7373- preview.appendChild(DOM.space());
7474- preview.appendChild(DOM.text(shortContent));
7575- if (isOID) {
7676- var oid = oids[content];
7777- if (oid) {
7878- if (oid.d) {
7979- preview.appendChild(DOM.space());
8080- var oidd = DOM.tag("span", "oid description");
8181- oidd.appendChild(DOM.text(oid.d));
8282- preview.appendChild(oidd);
8383- }
8484- if (oid.c) {
8585- preview.appendChild(DOM.space());
8686- var oidc = DOM.tag("span", "oid comment");
8787- oidc.appendChild(DOM.text("(" + oid.c + ")"));
8888- preview.appendChild(oidc);
8989- }
5454+export class ASN1DOM extends ASN1 {
5555+5656+ toDOM(spaces) {
5757+ spaces = spaces || '';
5858+ let isOID = (typeof oids === 'object') && (this.tag.isUniversal() && (this.tag.tagNumber == 0x06) || (this.tag.tagNumber == 0x0D));
5959+ let node = DOM.tag('li');
6060+ node.asn1 = this;
6161+ let head = DOM.tag('span', 'head');
6262+ const typeName = this.typeName().replace(/_/g, ' ');
6363+ if (this.def) {
6464+ if (this.def.id) {
6565+ head.appendChild(DOM.tag('span', 'name id', this.def.id));
6666+ head.appendChild(DOM.space());
6767+ }
6868+ if (this.def.name && this.def.name != typeName) {
6969+ head.appendChild(DOM.tag('span', 'name type', this.def.name));
7070+ head.appendChild(DOM.space());
7171+ }
7272+ if (this.def.mismatch) {
7373+ head.appendChild(DOM.tag('span', 'name type', '[?]'));
7474+ head.appendChild(DOM.space());
9075 }
9176 }
9292- head.appendChild(preview);
9393- content = DOM.breakLines(content, lineLength);
9494- content = content.replace(/</g, "<");
9595- content = content.replace(/\n/g, "<br>");
9696- }
9797- node.appendChild(head);
9898- this.node = node;
9999- this.head = head;
100100- var value = DOM.tag("div", "value");
101101- var s = "Offset: " + this.stream.pos + "<br>";
102102- s += "Length: " + this.header + "+";
103103- if (this.length >= 0)
104104- s += this.length;
105105- else
106106- s += (-this.length) + " (undefined)";
107107- if (this.tag.tagConstructed)
108108- s += "<br>(constructed)";
109109- else if ((this.tag.isUniversal() && ((this.tag.tagNumber == 0x03) || (this.tag.tagNumber == 0x04))) && (this.sub !== null))
110110- s += "<br>(encapsulates)";
111111- //TODO if (this.tag.isUniversal() && this.tag.tagNumber == 0x03) s += "Unused bits: "
112112- if (content !== null) {
113113- s += "<br>Value:<br><b>" + content + "</b>";
114114- if (isOID && oid) {
115115- if (oid.d) s += "<br>" + oid.d;
116116- if (oid.c) s += "<br>" + oid.c;
117117- if (oid.w) s += "<br>(warning!)";
7777+ head.appendChild(DOM.text(typeName));
7878+ let content;
7979+ try {
8080+ content = this.content(contentLength);
8181+ } catch (e) {
8282+ content = 'Cannot decode: ' + e;
11883 }
119119- }
120120- value.innerHTML = s;
121121- node.appendChild(value);
122122- var sub = DOM.tag("div", "sub");
123123- if (this.sub !== null) {
124124- spaces += '\xA0 ';
125125- for (var i = 0, max = this.sub.length; i < max; ++i)
126126- sub.appendChild(this.sub[i].toDOM(spaces));
127127- }
128128- node.appendChild(sub);
129129- head.onclick = function () {
130130- node.className = (node.className == "node collapsed") ? "node" : "node collapsed";
131131- };
132132- return node;
133133-};
134134-ASN1.prototype.fakeHover = function (current) {
135135- this.node.className += " hover";
136136- if (current)
137137- this.head.className += " hover";
138138-};
139139-ASN1.prototype.fakeOut = function (current) {
140140- var re = / ?hover/;
141141- this.node.className = this.node.className.replace(re, "");
142142- if (current)
143143- this.head.className = this.head.className.replace(re, "");
144144-};
145145-ASN1.prototype.toHexDOM_sub = function (node, className, stream, start, end) {
146146- if (start >= end)
147147- return;
148148- var sub = DOM.tag("span", className);
149149- sub.appendChild(DOM.text(
150150- stream.hexDump(start, end)));
151151- node.appendChild(sub);
152152-};
153153-ASN1.prototype.toHexDOM = function (root) {
154154- var node = DOM.tag("span", "hex");
155155- if (root === undefined) root = node;
156156- this.head.hexNode = node;
157157- this.head.onmouseover = function () { this.hexNode.className = "hexCurrent"; };
158158- this.head.onmouseout = function () { this.hexNode.className = "hex"; };
159159- node.asn1 = this;
160160- node.onmouseover = function () {
161161- var current = !root.selected;
162162- if (current) {
163163- root.selected = this.asn1;
164164- this.className = "hexCurrent";
8484+ let oid;
8585+ if (content !== null) {
8686+ let preview = DOM.tag('span', 'preview'),
8787+ shortContent;
8888+ if (isOID)
8989+ content = content.split('\n', 1)[0];
9090+ shortContent = (content.length > lineLength) ? content.substring(0, lineLength) + DOM.ellipsis : content;
9191+ preview.appendChild(DOM.space());
9292+ preview.appendChild(DOM.text(shortContent));
9393+ if (isOID) {
9494+ oid = oids[content];
9595+ if (oid) {
9696+ if (oid.d) {
9797+ preview.appendChild(DOM.space());
9898+ let oidd = DOM.tag('span', 'oid description', oid.d);
9999+ preview.appendChild(oidd);
100100+ }
101101+ if (oid.c) {
102102+ preview.appendChild(DOM.space());
103103+ let oidc = DOM.tag('span', 'oid comment', '(' + oid.c + ')');
104104+ preview.appendChild(oidc);
105105+ }
106106+ }
107107+ }
108108+ head.appendChild(preview);
109109+ content = DOM.breakLines(content, lineLength);
110110+ content = content.replace(/</g, '<');
111111+ content = content.replace(/\n/g, '<br>');
165112 }
166166- this.asn1.fakeHover(current);
167167- };
168168- node.onmouseout = function () {
169169- var current = (root.selected == this.asn1);
170170- this.asn1.fakeOut(current);
171171- if (current) {
172172- root.selected = null;
173173- this.className = "hex";
113113+ // add the li and details section for this node
114114+ let contentNode;
115115+ let childNode;
116116+ if (this.sub !== null) {
117117+ let details = DOM.tag('details');
118118+ details.setAttribute('open', '');
119119+ node.appendChild(details);
120120+ let summary = DOM.tag('summary', 'node');
121121+ details.appendChild(summary);
122122+ summary.appendChild(head);
123123+ contentNode = summary;
124124+ childNode = details;
125125+ } else {
126126+ contentNode = node;
127127+ contentNode.classList.add('node');
128128+ contentNode.appendChild(head);
129129+ }
130130+ this.node = contentNode;
131131+ this.head = head;
132132+ let value = DOM.tag('div', 'value');
133133+ let s = 'Offset: ' + this.stream.pos + '<br>';
134134+ s += 'Length: ' + this.header + '+';
135135+ if (this.length >= 0)
136136+ s += this.length;
137137+ else
138138+ s += (-this.length) + ' (undefined)';
139139+ if (this.tag.tagConstructed)
140140+ s += '<br>(constructed)';
141141+ else if ((this.tag.isUniversal() && ((this.tag.tagNumber == 0x03) || (this.tag.tagNumber == 0x04))) && (this.sub !== null))
142142+ s += '<br>(encapsulates)';
143143+ //TODO if (this.tag.isUniversal() && this.tag.tagNumber == 0x03) s += "Unused bits: "
144144+ if (content !== null) {
145145+ s += '<br>Value:<br><b>' + content + '</b>';
146146+ if (isOID && oid) {
147147+ if (oid.d) s += '<br>' + oid.d;
148148+ if (oid.c) s += '<br>' + oid.c;
149149+ if (oid.w) s += '<br>(warning!)';
150150+ }
174151 }
175175- };
176176- if (root == node) {
177177- var lineStart = this.posStart() & 0xF;
178178- if (lineStart != 0) {
179179- var skip = DOM.tag("span", "skip");
180180- var skipStr = '';
181181- for (var j = lineStart; j > 0; --j)
182182- skipStr += ' ';
183183- if (lineStart >= 8)
184184- skipStr += ' ';
185185- skip.innerText = skipStr;
186186- node.appendChild(skip);
152152+ value.innerHTML = s;
153153+ contentNode.appendChild(value);
154154+ if (this.sub !== null) {
155155+ let sub = DOM.tag('ul');
156156+ childNode.appendChild(sub);
157157+ spaces += '\xA0 ';
158158+ for (let i = 0, max = this.sub.length; i < max; ++i)
159159+ sub.appendChild(this.sub[i].toDOM(spaces));
187160 }
161161+ bindContextMenu(node);
162162+ return node;
188163 }
189189- this.toHexDOM_sub(node, "tag", this.stream, this.posStart(), this.posLen());
190190- this.toHexDOM_sub(node, (this.length >= 0) ? "dlen" : "ulen", this.stream, this.posLen(), this.posContent());
191191- if (this.sub === null) {
192192- var start = this.posContent();
193193- var end = this.posEnd();
194194- if (end - start < 10 * 16)
195195- node.appendChild(DOM.text(
196196- this.stream.hexDump(start, end)));
197197- else {
198198- var end1 = start + 5 * 16 - (start & 0xF);
199199- var start2 = end - 16 - (end & 0xF);
200200- node.appendChild(DOM.text(
201201- this.stream.hexDump(start, end1)));
202202- var sub = DOM.tag("span", "skip");
203203- sub.appendChild(DOM.text("\u2026 skipping " + (start2 - end1) + " bytes \u2026\n"));
204204- node.appendChild(sub);
205205- node.appendChild(DOM.text(
206206- this.stream.hexDump(start2, end)));
164164+ fakeHover(current) {
165165+ this.node.classList.add('hover');
166166+ if (current)
167167+ this.head.classList.add('hover');
168168+ }
169169+ fakeOut(current) {
170170+ this.node.classList.remove('hover');
171171+ if (current)
172172+ this.head.classList.remove('hover');
173173+ }
174174+ toHexDOM_sub(node, className, stream, start, end) {
175175+ if (start >= end)
176176+ return;
177177+ let sub = DOM.tag('span', className, stream.hexDump(start, end));
178178+ node.appendChild(sub);
179179+ }
180180+ toHexDOM(root, trim=true) {
181181+ let node = DOM.tag('span', 'hex');
182182+ if (root === undefined) root = node;
183183+ this.head.hexNode = node;
184184+ this.head.onmouseover = function () { this.hexNode.className = 'hexCurrent'; };
185185+ this.head.onmouseout = function () { this.hexNode.className = 'hex'; };
186186+ node.asn1 = this;
187187+ node.onmouseover = function (event) {
188188+ let current = !root.selected;
189189+ if (current) {
190190+ root.selected = this.asn1;
191191+ this.className = 'hexCurrent';
192192+ }
193193+ this.asn1.fakeHover(current);
194194+ event.stopPropagation();
195195+ };
196196+ node.onmouseout = function () {
197197+ let current = (root.selected == this.asn1);
198198+ this.asn1.fakeOut(current);
199199+ if (current) {
200200+ root.selected = null;
201201+ this.className = 'hex';
202202+ }
203203+ };
204204+ bindContextMenu(node);
205205+ if (root == node) {
206206+ let lineStart = this.posStart() & 0xF;
207207+ if (lineStart != 0) {
208208+ let skip = DOM.tag('span', 'skip');
209209+ let skipStr = '';
210210+ for (let j = lineStart; j > 0; --j)
211211+ skipStr += ' ';
212212+ if (lineStart >= 8)
213213+ skipStr += ' ';
214214+ skip.innerText = skipStr;
215215+ node.appendChild(skip);
216216+ }
207217 }
208208- } else if (this.sub.length > 0) {
209209- var first = this.sub[0];
210210- var last = this.sub[this.sub.length - 1];
211211- this.toHexDOM_sub(node, "intro", this.stream, this.posContent(), first.posStart());
212212- for (var i = 0, max = this.sub.length; i < max; ++i)
213213- node.appendChild(this.sub[i].toHexDOM(root));
214214- this.toHexDOM_sub(node, "outro", this.stream, last.posEnd(), this.posEnd());
215215- } else
216216- this.toHexDOM_sub(node, "outro", this.stream, this.posContent(), this.posEnd());
217217- return node;
218218-};
218218+ this.toHexDOM_sub(node, 'tag', this.stream, this.posStart(), this.posLen());
219219+ this.toHexDOM_sub(node, (this.length >= 0) ? 'dlen' : 'ulen', this.stream, this.posLen(), this.posContent());
220220+ if (this.sub === null) {
221221+ let start = this.posContent();
222222+ let end = this.posEnd();
223223+ if (!trim || end - start < 10 * 16)
224224+ node.appendChild(DOM.text(
225225+ this.stream.hexDump(start, end)));
226226+ else {
227227+ let end1 = start + 5 * 16 - (start & 0xF);
228228+ let start2 = end - 16 - (end & 0xF);
229229+ node.appendChild(DOM.text(this.stream.hexDump(start, end1)));
230230+ node.appendChild(DOM.tag('span', 'skip', '\u2026 skipping ' + (start2 - end1) + ' bytes \u2026\n'));
231231+ node.appendChild(DOM.text(this.stream.hexDump(start2, end)));
232232+ }
233233+ } else if (this.sub.length > 0) {
234234+ let first = this.sub[0];
235235+ let last = this.sub[this.sub.length - 1];
236236+ this.toHexDOM_sub(node, 'intro', this.stream, this.posContent(), first.posStart());
237237+ for (let i = 0, max = this.sub.length; i < max; ++i)
238238+ node.appendChild(this.sub[i].toHexDOM(root, trim));
239239+ this.toHexDOM_sub(node, 'outro', this.stream, last.posEnd(), this.posEnd());
240240+ } else
241241+ this.toHexDOM_sub(node, 'outro', this.stream, this.posContent(), this.posEnd());
242242+ return node;
243243+ }
244244+ static decode(stream, offset) {
245245+ return ASN1.decode(stream, offset, ASN1DOM);
246246+ }
219247220220-});
248248+}
+83
dumpASN1.js
···11+#!/usr/bin/env node
22+33+// usage:
44+// ./dumpASN1.js filename
55+// ./dumpASN1.js data:base64,MDMCAQFjLgQACgEACgEAAgEAAgEAAQEAoA+jDQQFTnRWZXIEBAEAAAAwCgQITmV0bG9nb24===
66+77+import * as fs from 'node:fs';
88+import { Base64 } from './base64.js';
99+import { ASN1 } from './asn1.js';
1010+import { Defs } from './defs.js';
1111+1212+const
1313+ colYellow = '\x1b[33m',
1414+ colBlue = '\x1b[34m',
1515+ colReset = '\x1b[0m',
1616+ reDataURI = /^data:(?:[a-z-]+[/][a-z.+-]+;)?base64,([A-Za-z0-9+/=\s]+)$/;
1717+1818+function print(value, indent) {
1919+ if (indent === undefined) indent = '';
2020+ const def = value.def;
2121+ let name = '';
2222+ if (def?.type) {
2323+ if (def.id) name += colBlue + def.id + colReset;
2424+ if (typeof def.type == 'object' && def.name) name = (name ? name + ' ' : '') + def.name;
2525+ if (def.mismatch) name = (name ? name + ' ' : '') + '[?]';
2626+ if (name) name += ' ';
2727+ }
2828+ let s = indent + name + colYellow + value.typeName() + colReset + ' @' + value.stream.pos;
2929+ if (value.length >= 0)
3030+ s += '+';
3131+ s += value.length;
3232+ if (value.tag.tagConstructed)
3333+ s += ' (constructed)';
3434+ else if ((value.tag.isUniversal() && ((value.tag.tagNumber == 0x03) || (value.tag.tagNumber == 0x04))) && (value.sub !== null))
3535+ s += ' (encapsulates)';
3636+ let content = value.content();
3737+ if (content)
3838+ s += ': ' + content.replace(/\n/g, '|');
3939+ s += '\n';
4040+ if (value.sub !== null) {
4141+ indent += ' ';
4242+ for (const subval of value.sub)
4343+ s += print(subval, indent);
4444+ }
4545+ return s;
4646+}
4747+4848+const filename = process.argv[2];
4949+const match = reDataURI.exec(filename);
5050+let content = match
5151+ ? Buffer.from(match[1])
5252+ : fs.readFileSync(filename);
5353+try { // try PEM first
5454+ content = Base64.unarmor(content);
5555+} catch (e) { // try DER/BER then
5656+}
5757+let result = ASN1.decode(content);
5858+content = null;
5959+const t0 = performance.now();
6060+if (process.argv.length == 5) {
6161+ Defs.match(result, Defs.moduleAndType(Defs.RFC[process.argv[3]], process.argv[4]));
6262+} else {
6363+ const types = Defs.commonTypes
6464+ .map(type => {
6565+ const stats = Defs.match(result, type);
6666+ return { type, match: stats.recognized / stats.total };
6767+ })
6868+ .sort((a, b) => b.match - a.match);
6969+ const t1 = performance.now();
7070+ console.log('Parsed in ' + (t1 - t0).toFixed(2) + ' ms; possible types:');
7171+ for (const t of types)
7272+ console.log((t.match * 100).toFixed(2).padStart(6) + '% ' + t.type.description);
7373+ Defs.match(result, types[0].type);
7474+ // const stats = Defs.match(result, types[0].type);
7575+ // console.log('Stats:', stats);
7676+ console.log('Parsed as:', result.def);
7777+ // const type = searchType(process.argv[2]);
7878+ // const stats = applyDef(result, type);
7979+}
8080+console.log(print(result));
8181+// console.log('Stats:', (stats.recognized * 100 / stats.total).toFixed(2) + '%');
8282+// // print(result, searchType(process.argv[2]), stats);
8383+// // console.log('Defs:', stats.defs);
+21
examples/cmpv2.b64
···11+CMPv2 example as found on Wireshark page.
22+33+Original link:
44+https://wiki.wireshark.org/CMP
55+66+Attachment found moved here:
77+https://wiki.wireshark.org/uploads/__moin_import__/attachments/SampleCaptures/cmp_IR_sequence_OpenSSL-Cryptlib.pcap
88+99+begin-base64 644 cmpv2.der
1010+MIICPjCB1QIBAqQCMACkRjBEMQswCQYDVQQGEwJERTEMMAoGA1UEChMDTlNOMREwDwYDVQQLEwhQ
1111+RyBSREUgMzEUMBIGA1UEAxMLTWFydGluJ3MgQ0GgERgPMjAxMDA3MDUwNzM1MzhaoTwwOgYJKoZI
1212+hvZ9B0INMC0EEJ5EpSD3zKjvmzHEK5+aoAAwCQYFKw4DAhoFAAICAfQwCgYIKwYBBQUIAQKiCwQJ
1313+b/KGO0ILNJqApBIEEJGOKFG/9crkwU+z/I5ICa6lEgQQnnbd7EB2QjRCwOHt9QWdBKCCAUkwggFF
1414+MIIBQTCBqAIBADCBoqaBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAqVTOtjEEYELkomc3sMOy
1515+Too5a9YeC91IMn52cVx7doY4AeO6J9e8p+CtWNbzVF8aRgHUhh31m+/X3MkQOaY5i8nF33uxAxDL
1616+MDXttHjsqrF/tsgYuuHSs/Znz4PA1kLkdhKE9DLiGlCFaJH5QY5Hzl6bcS3ApuWCny0RRzIA1/cC
1717+AwEAAaGBkzANBgkqhkiG9w0BAQUFAAOBgQArOldjg75fDx7BaFp0oAknLDREvB1KyE+BV96R+lB+
1818+tRRhwv3dyc/GTvRw4GtaeDjWCjNPaDCl9ZvvVljaR2aMZvhaQV+DUmCMjFSP3DPiGuszBA6R2azX
1919+NKtnpJ3SGx2vk0+Iv05tXLhdnqQJZs5a3S3R30kn4Vw+4WQm3kb0fKAXAxUA9K8u+7hv5Rg6GDn6
2020+aoPxbUo6fpU=
2121+====
+9
examples/cms-password.p7m
···11+This is a PKCS#7/CMS encrypted with passwod.
22+$ echo content | openssl cms -encrypt -pwri_password test -aes256 -outform pem -out examples/cms-password.p7m
33+-----BEGIN CMS-----
44+MIHYBgkqhkiG9w0BBwOggcowgccCAQMxgYOjgYACAQCgGwYJKoZIhvcNAQUMMA4E
55+CED/DSxXMtH6AgIIADAsBgsqhkiG9w0BCRADCTAdBglghkgBZQMEASoEEDIQbJMC
66+Sfb3LpwHduj/meQEMKwrwq5M4V0stztm6OUTAsFY2zKDY20SApwSEeEcAh9TM42E
77+1palnHeqHTBpC8pIpjA8BgkqhkiG9w0BBwEwHQYJYIZIAWUDBAEqBBByt+scPrdM
88+giR7WUOJyB3hgBDcD3UDMtZSep8X/3yy1/Yq
99+-----END CMS-----
+12
examples/crl-rfc5280.b64
···11+CRL example from RFC5280 as found here:
22+https://csrc.nist.gov/projects/pki-testing/sample-certificates-and-crls
33+44+begin-base64 644 crl-rfc5280.der
55+MIIBYDCBygIBATANBgkqhkiG9w0BAQUFADBDMRMwEQYKCZImiZPyLGQBGRYDY29tMRcwFQYKCZIm
66+iZPyLGQBGRYHZXhhbXBsZTETMBEGA1UEAxMKRXhhbXBsZSBDQRcNMDUwMjA1MTIwMDAwWhcNMDUw
77+MjA2MTIwMDAwWjAiMCACARIXDTA0MTExOTE1NTcwM1owDDAKBgNVHRUEAwoBAaAvMC0wHwYDVR0j
88+BBgwFoAUCGivhTPIOUp6+IKTjnBqSiCELDIwCgYDVR0UBAMCAQwwDQYJKoZIhvcNAQEFBQADgYEA
99+ItwYffcIzsx10NBqm60Q9HYjtIFutW2+DvsVFGzIF20f7pAXom9g5L2qjFXejoRvkvifEBInr0rU
1010+L4XiNkR9qqNMJTgV/wD9Pn7uPSYS69jnK2LiK8NGgO94gtEVxtCccmrLznrtZ5mLbnCBfUNCdMGm
1111+r8FVF6IzTNYGmCuk/C4=
1212+====
+8
examples/ldapmessage.b64
···11+LDAPMessage example as found on ldap.com.
22+33+Original link:
44+https://ldap.com/ldapv3-wire-protocol-reference-ldap-message/
55+66+begin-base64 644 ldapmessage.der
77+MDUCAQVKEWRjPWV4YW1wbGUsZGM9Y29toB0wGwQWMS4yLjg0MC4xMTM1NTYuMS40LjgwNQEB/w==
88+====
···11// Hex JavaScript decoder
22-// Copyright (c) 2008-2021 Lapo Luchini <lapo@lapo.it>
22+// Copyright (c) 2008 Lapo Luchini <lapo@lapo.it>
3344// Permission to use, copy, modify, and/or distribute this software for any
55// purpose with or without fee is hereby granted, provided that the above
66// copyright notice and this permission notice appear in all copies.
77-//
77+//
88// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
99// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1010// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
···1313// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1414// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15151616-(typeof define != 'undefined' ? define : function (factory) { 'use strict';
1717- if (typeof module == 'object') module.exports = factory();
1818- else window.hex = factory();
1919-})(function () {
2020-"use strict";
1616+const
1717+ haveU8 = (typeof Uint8Array == 'function');
1818+1919+let decoder; // populated on first usage
21202222-var Hex = {},
2323- decoder, // populated on first usage
2424- haveU8 = (typeof Uint8Array == 'function');
2121+export class Hex {
25222626-/**
2727- * Decodes an hexadecimal value.
2828- * @param {string|Array|Uint8Array} a - a string representing hexadecimal data, or an array representation of its charcodes
2929- */
3030-Hex.decode = function(a) {
3131- var isString = (typeof a == 'string');
3232- var i;
3333- if (decoder === undefined) {
3434- var hex = "0123456789ABCDEF",
3535- ignore = " \f\n\r\t\u00A0\u2028\u2029";
3636- decoder = [];
3737- for (i = 0; i < 16; ++i)
3838- decoder[hex.charCodeAt(i)] = i;
3939- hex = hex.toLowerCase();
4040- for (i = 10; i < 16; ++i)
4141- decoder[hex.charCodeAt(i)] = i;
4242- for (i = 0; i < ignore.length; ++i)
4343- decoder[ignore.charCodeAt(i)] = -1;
4444- }
4545- var out = haveU8 ? new Uint8Array(a.length >> 1) : [],
4646- bits = 0,
4747- char_count = 0,
4848- len = 0;
4949- for (i = 0; i < a.length; ++i) {
5050- var c = isString ? a.charCodeAt(i) : a[i];
5151- c = decoder[c];
5252- if (c == -1)
5353- continue;
5454- if (c === undefined)
5555- throw 'Illegal character at offset ' + i;
5656- bits |= c;
5757- if (++char_count >= 2) {
5858- out[len++] = bits;
5959- bits = 0;
6060- char_count = 0;
6161- } else {
6262- bits <<= 4;
2323+ /**
2424+ * Decodes an hexadecimal value.
2525+ * @param {string|Array|Uint8Array} a - a string representing hexadecimal data, or an array representation of its charcodes
2626+ */
2727+ static decode(a) {
2828+ let isString = (typeof a == 'string');
2929+ let i;
3030+ if (decoder === undefined) {
3131+ let hex = '0123456789ABCDEF',
3232+ ignore = ' \f\n\r\t\u00A0\u2028\u2029';
3333+ decoder = [];
3434+ for (i = 0; i < 16; ++i)
3535+ decoder[hex.charCodeAt(i)] = i;
3636+ hex = hex.toLowerCase();
3737+ for (i = 10; i < 16; ++i)
3838+ decoder[hex.charCodeAt(i)] = i;
3939+ for (i = 0; i < ignore.length; ++i)
4040+ decoder[ignore.charCodeAt(i)] = -1;
4141+ }
4242+ let out = haveU8 ? new Uint8Array(a.length >> 1) : [],
4343+ bits = 0,
4444+ char_count = 0,
4545+ len = 0;
4646+ for (i = 0; i < a.length; ++i) {
4747+ let c = isString ? a.charCodeAt(i) : a[i];
4848+ c = decoder[c];
4949+ if (c == -1)
5050+ continue;
5151+ if (c === undefined)
5252+ throw 'Illegal character at offset ' + i;
5353+ bits |= c;
5454+ if (++char_count >= 2) {
5555+ out[len++] = bits;
5656+ bits = 0;
5757+ char_count = 0;
5858+ } else {
5959+ bits <<= 4;
6060+ }
6361 }
6262+ if (char_count)
6363+ throw 'Hex encoding incomplete: 4 bits missing';
6464+ if (haveU8 && out.length > len) // in case it was originally longer because of ignored characters
6565+ out = out.subarray(0, len);
6666+ return out;
6467 }
6565- if (char_count)
6666- throw "Hex encoding incomplete: 4 bits missing";
6767- if (haveU8 && out.length > len) // in case it was originally longer because of ignored characters
6868- out = out.subarray(0, len);
6969- return out;
7070-};
71687272-return Hex;
7373-7474-});
6969+}
···11-(typeof define != 'undefined' ? define : function (factory) { 'use strict';
22- if (typeof module == 'object') factory(function (name) { return require(name); });
33- else factory(function (name) { return window[name.substring(2)]; });
44-})(function (require) {
55-"use strict";
11+import './theme.js';
22+import { ASN1DOM } from './dom.js';
33+import { Base64 } from './base64.js';
44+import { Hex } from './hex.js';
55+import { Defs } from './defs.js';
66+import { tags } from './tags.js';
6777-var ASN1 = require('./asn1'),
88- Base64 = require('./base64'),
99- Hex = require('./hex'),
88+const
109 maxLength = 10240,
1110 reHex = /^\s*(?:[0-9A-Fa-f][0-9A-Fa-f]\s*)+$/,
1211 tree = id('tree'),
1312 dump = id('dump'),
1414- wantHex = id('wantHex'),
1313+ wantHex = checkbox('wantHex'),
1414+ trimHex = checkbox('trimHex'),
1515+ wantDef = checkbox('wantDef'),
1516 area = id('area'),
1617 file = id('file'),
1718 examples = id('examples'),
1818- hash = null;
1919+ selectDefs = id('definitions'),
2020+ selectTag = id('tags');
19212020-require('./dom'); // side effect: augment ASN1
2222+let hash = null;
2323+2124if (!window.console || !window.console.log) // IE8 with closed developer tools
2225 window.console = { log: function () {} };
2326function id(elem) {
2427 return document.getElementById(elem);
2528}
2629function text(el, string) {
2727- if ('textContent' in el)
2828- el.textContent = string;
2929- else
3030- el.innerText = string;
3030+ if ('textContent' in el) el.textContent = string;
3131+ else el.innerText = string;
3132}
3232-function decode(der, offset) {
3333- offset = offset || 0;
3333+function checkbox(name) {
3434+ const el = id(name);
3535+ const cfg = localStorage.getItem(name);
3636+ if (cfg === 'false')
3737+ el.checked = false;
3838+ el.onchange = () => localStorage.setItem(name, el.checked);
3939+ return el;
4040+}
4141+function show(asn1) {
3442 tree.innerHTML = '';
3543 dump.innerHTML = '';
4444+ let ul = document.createElement('ul');
4545+ ul.className = 'treecollapse';
4646+ tree.appendChild(ul);
4747+ ul.appendChild(asn1.toDOM());
4848+ if (wantHex.checked) dump.appendChild(asn1.toHexDOM(undefined, trimHex.checked));
4949+}
5050+export function decode(der, offset) {
5151+ offset = offset || 0;
3652 try {
3737- var asn1 = ASN1.decode(der, offset);
3838- tree.appendChild(asn1.toDOM());
3939- if (wantHex.checked)
4040- dump.appendChild(asn1.toHexDOM());
4141- var b64 = (der.length < maxLength) ? asn1.toB64String() : '';
4242- if (area.value === '')
4343- area.value = Base64.pretty(b64);
5353+ const asn1 = ASN1DOM.decode(der, offset);
5454+ if (wantDef.checked) {
5555+ selectDefs.innerHTML = '';
5656+ const types = Defs.commonTypes
5757+ .map(type => {
5858+ const stats = Defs.match(asn1, type);
5959+ return { type, match: stats.recognized / stats.total };
6060+ })
6161+ .sort((a, b) => b.match - a.match);
6262+ for (const t of types) {
6363+ t.element = document.createElement('option');
6464+ t.element.innerText = (t.match * 100).toFixed(1) + '% ' + t.type.description;
6565+ selectDefs.appendChild(t.element);
6666+ }
6767+ let not = document.createElement('option');
6868+ not.innerText = 'no definition';
6969+ selectDefs.appendChild(not);
7070+ Defs.match(asn1, types[0].type);
7171+ selectDefs.onchange = () => {
7272+ for (const t of types) {
7373+ if (t.element == selectDefs.selectedOptions[0]) {
7474+ Defs.match(asn1, t.type);
7575+ show(asn1);
7676+ return;
7777+ }
7878+ }
7979+ Defs.match(asn1, null);
8080+ show(asn1);
8181+ };
8282+ } else
8383+ selectDefs.innerHTML = '<option>no definition</option>';
8484+ show(asn1);
8585+ let b64 = der.length < maxLength ? asn1.toB64String() : '';
8686+ if (area.value === '') area.value = Base64.pretty(b64);
4487 try {
4588 window.location.hash = hash = '#' + b64;
4646- } catch (e) { // fails with "Access Denied" on IE with URLs longer than ~2048 chars
8989+ } catch (e) {
9090+ // fails with "Access Denied" on IE with URLs longer than ~2048 chars
4791 window.location.hash = hash = '#';
4892 }
4949- var endOffset = asn1.posEnd();
9393+ let endOffset = asn1.posEnd();
5094 if (endOffset < der.length) {
5151- var p = document.createElement('p');
9595+ let p = document.createElement('p');
5296 p.innerText = 'Input contains ' + (der.length - endOffset) + ' more bytes to decode.';
5353- var button = document.createElement('button');
9797+ let button = document.createElement('button');
5498 button.innerText = 'try to decode';
5599 button.onclick = function () {
56100 decode(der, endOffset);
···62106 text(tree, e);
63107 }
64108}
6565-function decodeText(val) {
109109+export function decodeText(val) {
66110 try {
6767- var der = reHex.test(val) ? Hex.decode(val) : Base64.unarmor(val);
111111+ let der = reHex.test(val) ? Hex.decode(val) : Base64.unarmor(val);
68112 decode(der);
69113 } catch (e) {
70114 text(tree, e);
71115 dump.innerHTML = '';
72116 }
73117}
7474-function decodeBinaryString(str) {
7575- var der;
118118+export function decodeBinaryString(str) {
119119+ let der;
76120 try {
7777- if (reHex.test(str))
7878- der = Hex.decode(str);
7979- else if (Base64.re.test(str))
8080- der = Base64.unarmor(str);
8181- else
8282- der = str;
121121+ if (reHex.test(str)) der = Hex.decode(str);
122122+ else if (Base64.re.test(str)) der = Base64.unarmor(str);
123123+ else der = str;
83124 decode(der);
84125 } catch (e) {
85126 text(tree, 'Cannot decode file.');
···87128 }
88129}
89130// set up buttons
9090-id('butDecode').onclick = function () { decodeText(area.value); };
9191-id('butClear').onclick = function () {
9292- area.value = '';
9393- file.value = '';
9494- tree.innerHTML = '';
9595- dump.innerHTML = '';
9696- hash = window.location.hash = '';
9797-};
9898-id('butExample').onclick = function () {
9999- console.log('Loading example:', examples.value);
100100- var request = new XMLHttpRequest();
101101- request.open('GET', 'examples/' + examples.value, true);
102102- request.onreadystatechange = function() {
103103- if (this.readyState !== 4)
104104- return;
105105- if (this.status >= 200 && this.status < 400) {
106106- area.value = this.responseText;
107107- decodeText(this.responseText);
108108- } else {
109109- console.log('Error loading example.');
110110- }
111111- };
112112- request.send();
131131+const butClickHandlers = {
132132+ butDecode: () => {
133133+ decodeText(area.value);
134134+ },
135135+ butClear: () => {
136136+ area.value = '';
137137+ file.value = '';
138138+ tree.innerHTML = '';
139139+ dump.innerHTML = '';
140140+ selectDefs.innerHTML = '';
141141+ hash = window.location.hash = '';
142142+ },
143143+ butExample: () => {
144144+ console.log('Loading example:', examples.value);
145145+ let request = new XMLHttpRequest();
146146+ request.open('GET', 'examples/' + examples.value, true);
147147+ request.onreadystatechange = function () {
148148+ if (this.readyState !== 4) return;
149149+ if (this.status >= 200 && this.status < 400) {
150150+ area.value = this.responseText;
151151+ decodeText(this.responseText);
152152+ } else {
153153+ console.log('Error loading example.');
154154+ }
155155+ };
156156+ request.send();
157157+ },
113158};
159159+for (const [name, onClick] of Object.entries(butClickHandlers)) {
160160+ let elem = id(name);
161161+ if (elem)
162162+ elem.onclick = onClick;
163163+}
114164// this is only used if window.FileReader
115165function read(f) {
116166 area.value = ''; // clear text area, will get b64 content
117117- var r = new FileReader();
167167+ let r = new FileReader();
118168 r.onloadend = function () {
119119- if (r.error)
120120- alert("Your browser couldn't read the specified file (error code " + r.error.code + ").");
121121- else
122122- decodeBinaryString(r.result);
169169+ if (r.error) alert("Your browser couldn't read the specified file (error code " + r.error.code + ').');
170170+ else decodeBinaryString(r.result);
123171 };
124172 r.readAsBinaryString(f);
125173}
126174function load() {
127127- if (file.files.length === 0)
128128- alert("Select a file to load first.");
129129- else
130130- read(file.files[0]);
175175+ if (file.files.length === 0) alert('Select a file to load first.');
176176+ else read(file.files[0]);
131177}
132178function loadFromHash() {
133179 if (window.location.hash && window.location.hash != hash) {
···135181 // Firefox is not consistent with other browsers and returns an
136182 // already-decoded hash string so we risk double-decoding here,
137183 // but since % is not allowed in base64 nor hexadecimal, it's ok
138138- var val = decodeURIComponent(hash.substr(1));
139139- if (val.length)
140140- decodeText(val);
184184+ let val = decodeURIComponent(hash.substr(1));
185185+ if (val.length) decodeText(val);
141186 }
142187}
143188function stop(e) {
···146191}
147192function dragAccept(e) {
148193 stop(e);
149149- if (e.dataTransfer.files.length > 0)
150150- read(e.dataTransfer.files[0]);
194194+ if (e.dataTransfer.files.length > 0) read(e.dataTransfer.files[0]);
151195}
152196// main
153153-if ('onhashchange' in window)
154154- window.onhashchange = loadFromHash;
197197+if ('onhashchange' in window) window.onhashchange = loadFromHash;
155198loadFromHash();
156199document.ondragover = stop;
157200document.ondragleave = stop;
158158-if ('FileReader' in window && 'readAsBinaryString' in (new FileReader())) {
201201+if ('FileReader' in window && 'readAsBinaryString' in new FileReader()) {
159202 file.style.display = 'block';
160203 file.onchange = load;
161204 document.ondrop = dragAccept;
162205}
163163-164164-});
206206+for (let tag in tags) {
207207+ let date = tags[tag];
208208+ let el = document.createElement('option');
209209+ el.value = tag;
210210+ el.innerText = date + ' ' + tag;
211211+ selectTag.appendChild(el);
212212+}
213213+selectTag.onchange = function (ev) {
214214+ let tag = ev.target.selectedOptions[0].value;
215215+ window.location.href = 'https://rawcdn.githack.com/lapo-luchini/asn1js/' + tag + '/index.html';
216216+};
+89-90
int10.js
···11// Big integer base-10 printing library
22-// Copyright (c) 2008-2021 Lapo Luchini <lapo@lapo.it>
22+// Copyright (c) 2008 Lapo Luchini <lapo@lapo.it>
3344// Permission to use, copy, modify, and/or distribute this software for any
55// purpose with or without fee is hereby granted, provided that the above
66// copyright notice and this permission notice appear in all copies.
77-//
77+//
88// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
99// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1010// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
···1313// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1414// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15151616-(typeof define != 'undefined' ? define : function (factory) { 'use strict';
1717- if (typeof module == 'object') module.exports = factory();
1818- else window.int10 = factory();
1919-})(function () {
2020-"use strict";
1616+/** Biggest 10^n integer that can still fit 2^53 when multiplied by 256. */
1717+const max = 10000000000000;
21182222-var max = 10000000000000; // biggest 10^n integer that can still fit 2^53 when multiplied by 256
1919+export class Int10 {
2020+ /**
2121+ * Arbitrary length base-10 value.
2222+ * @param {number} value - Optional initial value (will be 0 otherwise).
2323+ */
2424+ constructor(value) {
2525+ this.buf = [+value || 0];
2626+ }
23272424-/**
2525- * Arbitrary length base-10 value.
2626- * @param {number} value - Optional initial value (will be 0 otherwise).
2727- */
2828-function Int10(value) {
2929- this.buf = [+value || 0];
3030-}
2828+ /**
2929+ * Multiply value by m and add c.
3030+ * @param {number} m - multiplier, must be 0<m<=256
3131+ * @param {number} c - value to add, must be c>=0
3232+ */
3333+ mulAdd(m, c) {
3434+ // assert(m > 0)
3535+ // assert(m <= 256)
3636+ // assert(c >= 0)
3737+ let b = this.buf,
3838+ l = b.length,
3939+ i, t;
4040+ for (i = 0; i < l; ++i) {
4141+ t = b[i] * m + c;
4242+ if (t < max)
4343+ c = 0;
4444+ else {
4545+ c = 0|(t / max);
4646+ t -= c * max;
4747+ }
4848+ b[i] = t;
4949+ }
5050+ if (c > 0)
5151+ b[i] = c;
5252+ }
31533232-/**
3333- * Multiply value by m and add c.
3434- * @param {number} m - multiplier, must be < =256
3535- * @param {number} c - value to add
3636- */
3737-Int10.prototype.mulAdd = function (m, c) {
3838- // assert(m <= 256)
3939- var b = this.buf,
4040- l = b.length,
4141- i, t;
4242- for (i = 0; i < l; ++i) {
4343- t = b[i] * m + c;
4444- if (t < max)
4545- c = 0;
4646- else {
4747- c = 0|(t / max);
4848- t -= c * max;
5454+ /**
5555+ * Subtract value.
5656+ * @param {number} c - value to subtract
5757+ */
5858+ sub(c) {
5959+ let b = this.buf,
6060+ l = b.length,
6161+ i, t;
6262+ for (i = 0; i < l; ++i) {
6363+ t = b[i] - c;
6464+ if (t < 0) {
6565+ t += max;
6666+ c = 1;
6767+ } else
6868+ c = 0;
6969+ b[i] = t;
4970 }
5050- b[i] = t;
7171+ while (b[b.length - 1] === 0)
7272+ b.pop();
5173 }
5252- if (c > 0)
5353- b[i] = c;
5454-};
55745656-/**
5757- * Subtract value.
5858- * @param {number} c - value to subtract
5959- */
6060-Int10.prototype.sub = function (c) {
6161- var b = this.buf,
6262- l = b.length,
6363- i, t;
6464- for (i = 0; i < l; ++i) {
6565- t = b[i] - c;
6666- if (t < 0) {
6767- t += max;
6868- c = 1;
6969- } else
7070- c = 0;
7171- b[i] = t;
7575+ /**
7676+ * Convert to decimal string representation.
7777+ * @param {number} [base=10] - optional value, only value accepted is 10
7878+ * @returns {string} The decimal string representation.
7979+ */
8080+ toString(base = 10) {
8181+ if (base != 10)
8282+ throw new Error('only base 10 is supported');
8383+ let b = this.buf,
8484+ s = b[b.length - 1].toString();
8585+ for (let i = b.length - 2; i >= 0; --i)
8686+ s += (max + b[i]).toString().substring(1);
8787+ return s;
7288 }
7373- while (b[b.length - 1] === 0)
7474- b.pop();
7575-};
76897777-/**
7878- * Convert to decimal string representation.
7979- * @param {*} base - optional value, only value accepted is 10
8080- */
8181-Int10.prototype.toString = function (base) {
8282- if ((base || 10) != 10)
8383- throw 'only base 10 is supported';
8484- var b = this.buf,
8585- s = b[b.length - 1].toString();
8686- for (var i = b.length - 2; i >= 0; --i)
8787- s += (max + b[i]).toString().substring(1);
8888- return s;
8989-};
9090+ /**
9191+ * Convert to Number value representation.
9292+ * Will probably overflow 2^53 and thus become approximate.
9393+ * @returns {number} The numeric value.
9494+ */
9595+ valueOf() {
9696+ let b = this.buf,
9797+ v = 0;
9898+ for (let i = b.length - 1; i >= 0; --i)
9999+ v = v * max + b[i];
100100+ return v;
101101+ }
901029191-/**
9292- * Convert to Number value representation.
9393- * Will probably overflow 2^53 and thus become approximate.
9494- */
9595-Int10.prototype.valueOf = function () {
9696- var b = this.buf,
9797- v = 0;
9898- for (var i = b.length - 1; i >= 0; --i)
9999- v = v * max + b[i];
100100- return v;
101101-};
102102-103103-/**
104104- * Return value as a simple Number (if it is <= 10000000000000), or return this.
105105- */
106106-Int10.prototype.simplify = function () {
107107- var b = this.buf;
108108- return (b.length == 1) ? b[0] : this;
109109-};
110110-111111-return Int10;
103103+ /**
104104+ * Return value as a simple Number (if it is <= 10000000000000), or return this.
105105+ * @returns {number | Int10} The simplified value.
106106+ */
107107+ simplify() {
108108+ let b = this.buf;
109109+ return (b.length == 1) ? b[0] : this;
110110+ }
112111113113-});
112112+}
···11-#/bin/sh
22-URL='https://www.cs.auckland.ac.nz/~pgut001/dumpasn1.cfg'
33-if [ -x /usr/bin/fetch ]; then
44- /usr/bin/fetch -m --no-verify-peer $URL
55-elif [ -x /usr/bin/wget ]; then
66- /usr/bin/wget -N --no-check-certificate $URL
77-elif [ ! -r dumpasn1.cfg ]; then
88- echo Please download $URL in this directory.
99- exit 1
1010-fi
1111-cat dumpasn1.cfg | \
1212-tr -d '\r' | \
1313-awk -v url="$URL" '
1414- function clean() {
1515- oid = "";
1616- comment = "";
1717- description = "";
1818- warning = "";
1919- }
2020- BEGIN {
2121- FS = "= *";
2222- apos = sprintf("%c", 39);
2323- clean();
2424- print "// Converted from: " url;
2525- print "// which is made by Peter Gutmann and whose license states:";
2626- print "// You can use this code in whatever way you want,";
2727- print "// as long as you don" apos "t try to claim you wrote it.";
2828- print "(typeof define != " apos "undefined" apos " ? define : function (factory) { " apos "use strict" apos ";";
2929- print " if (typeof module == " apos "object" apos ") module.exports = factory();";
3030- print " else window.oids = factory();";
3131- print "})(function () {";
3232- print apos "use strict" apos ";";
3333- print "return {";
3434- }
3535- /^OID/ { oid = $2; }
3636- /^Comment/ { comment = $2; }
3737- /^Description/ { description = $2; }
3838- /^Warning/ { warning = ", \"w\": true"; }
3939- /^$/ {
4040- if (length(oid) > 0) {
4141- gsub(" ", ".", oid);
4242- gsub("\"", "\\\"", description);
4343- gsub("\"", "\\\"", comment);
4444- if (++seen[oid] > 1)
4545- print "Duplicate OID in line " NR ": " oid > "/dev/stderr";
4646- else
4747- printf "\"%s\": { \"d\": \"%s\", \"c\": \"%s\"%s },\n", oid, description, comment, warning;
4848- clean();
4949- }
5050- }
5151- END {
5252- print "\"END\": \"\""
5353- print "};});"
5454- }
5555-' >oids.js
5656-echo Conversion completed.
+49
updateOID.sh
···11+#/bin/sh
22+URL='https://www.cs.auckland.ac.nz/~pgut001/dumpasn1.cfg'
33+if [ -x /usr/bin/fetch ]; then
44+ /usr/bin/fetch -m --no-verify-peer $URL
55+elif [ -x /usr/bin/wget ]; then
66+ /usr/bin/wget -N --no-check-certificate $URL
77+elif [ ! -r dumpasn1.cfg ]; then
88+ echo Please download $URL in this directory.
99+ exit 1
1010+fi
1111+cat dumpasn1.cfg | \
1212+tr -d '\r' | \
1313+awk -v apos="'" -v q='"' -v url="$URL" '
1414+ function clean() {
1515+ oid = "";
1616+ comment = "";
1717+ description = "";
1818+ warning = "";
1919+ }
2020+ BEGIN {
2121+ FS = "= *";
2222+ clean();
2323+ print "// Converted from: " url;
2424+ print "// which is made by Peter Gutmann and whose license states:";
2525+ print "// You can use this code in whatever way you want,";
2626+ print "// as long as you don" apos "t try to claim you wrote it.";
2727+ print "export const oids = {";
2828+ }
2929+ /^OID/ { oid = $2; }
3030+ /^Comment/ { comment = $2; }
3131+ /^Description/ { description = $2; }
3232+ /^Warning/ { warning = ", \"w\": true"; }
3333+ /^$/ {
3434+ if (length(oid) > 0) {
3535+ gsub(" ", ".", oid);
3636+ gsub("\"", "\\\"", description);
3737+ gsub("\"", "\\\"", comment);
3838+ if (++seen[oid] > 1)
3939+ print "Duplicate OID in line " NR ": " oid > "/dev/stderr";
4040+ else
4141+ printf "\"%s\": { \"d\": \"%s\", \"c\": \"%s\"%s },\n", oid, description, comment, warning;
4242+ clean();
4343+ }
4444+ }
4545+ END {
4646+ print "};"
4747+ }
4848+' >oids.js
4949+echo Conversion completed.
+32
updateRFC.sh
···11+#/bin/sh
22+RFCs="5280 5208 3369 3161 2986 4211 4210 8017 4511"
33+downloadRFC() {
44+ URL="https://www.ietf.org/rfc/rfc$1.txt"
55+ if [ -x /usr/bin/fetch ]; then
66+ /usr/bin/fetch -m --no-verify-peer $URL
77+ elif [ -x /usr/bin/wget ]; then
88+ /usr/bin/wget -N --no-check-certificate $URL
99+ elif [ ! -r dumpasn1.cfg ]; then
1010+ echo Please download $URL in this directory.
1111+ exit 1
1212+ fi
1313+}
1414+echo '{}' > rfcdef.json # start from scratch
1515+mkdir -p rfc
1616+cd rfc
1717+for n in $RFCs; do
1818+ downloadRFC $n
1919+ ../parseRFC.js rfc$n.txt ../rfcdef.json
2020+done
2121+cd ..
2222+{
2323+ echo "// content parsed from ASN.1 definitions as found in the following RFCs: $RFCs"
2424+ echo "// Copyright (C) The IETF Trust (2008)"
2525+ echo "// as far as I can tell this file is allowed under the following clause:"
2626+ echo "// It is acceptable under the current IETF rules (RFC 5378) to modify extracted code if necessary."
2727+ echo "// https://trustee.ietf.org/about/faq/#reproducing-rfcs"
2828+ echo -n "export const rfcdef = "
2929+ cat rfcdef.json
3030+ echo ";"
3131+} > rfcdef.js
3232+echo Conversion completed.