···11+# ChangeLog
22+33+## 2.1.0 - 2025-08-03
44+55+### Changed
66+77+- when fields are CHOICEs now both the field name and the choice name are shown (fixes GitHub #102)
88+- upgrade minimum NodeJS version supported from 12.20.0 to 14.6.0 due to usage of ?. and ?? operators in defs.js (ECMAScript 2020); older code is still linted against ECMAScript 2015 for now
99+1010+### Added
1111+1212+- add tests to check expected decoding
1313+1414+## 2.0.6 - 2025-07-29
1515+1616+### Added
1717+1818+- add proper support for standard Base64 (we previously only supported Base64url) (fixes GitHub #99)
1919+- improve test harness
2020+2121+## 2.0.5 - 2025-04-12
2222+2323+### Added
2424+2525+- add `index-local.html` for local `file://` usage without needing a web server
2626+- add definitions support for `LDAPMessage`
2727+- #TODO continue producing old ChangeLog entries
+1-1
LICENSE
···11ISC License
2233-Copyright (c) 2008-2022 Lapo Luchini <lapo@lapo.it>
33+Copyright (c) 2008-2025 Lapo Luchini <lapo@lapo.it>
4455Permission to use, copy, modify, and/or distribute this software for any
66purpose with or without fee is hereby granted, provided that the above
+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-2022 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···6695- extended tag support added by [Pรฉter Budai](https://www.peterbudai.eu/)
6796- patches by [Gergely Nagy](https://github.com/ngg)
6897- 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)
6910070101links
71102-----
7210373104- [official website](https://lapo.it/asn1js/)
105105+- [dedicated domain](https://asn1js.eu/)
74106- [InDefero tracker](http://idf.lapo.it/p/asn1js/)
75107- [GitHub mirror](https://github.com/lapo-luchini/asn1js)
108108+- [ChangeLog on GitHub](https://github.com/lapo-luchini/asn1js/blob/trunk/CHANGELOG.md)
76109- [Ohloh code stats](https://www.openhub.net/p/asn1js)
+586-526
asn1.js
···11// ASN.1 JavaScript decoder
22-// Copyright (c) 2008-2022 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",
1919+const
2020+ ellipsis = '\u2026',
2521 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)?)?$/,
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\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- }
6565- }
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);
7979- }
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);
8686- }
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;
9494- }
9595- return true;
9696-};
9797-Stream.prototype.parseStringISO = function (start, end, maxLength) {
9898- var s = "";
9999- for (var i = start; i < end; ++i)
100100- s += String.fromCharCode(this.get(i));
101101- return { size: s.length, str: stringCut(s, maxLength) };
102102-};
103103-var tableT61 = [
104104- ['', ''],
105105- ['AEIOUaeiou', 'รรรรรร รจรฌรฒรน'], // Grave
106106- ['ACEILNORSUYZacegilnorsuyz', 'รฤรรฤนลรลลรรลนรกฤรฉฤฃรญฤบลรณลลรบรฝลบ'], // Acute
107107- ['ACEGHIJOSUWYaceghijosuwy', 'รฤรฤฤครฤดรลรลดลถรขฤรชฤฤฅรฎฤตรดลรปลตลท'], // Circumflex
108108- ['AINOUainou', 'รฤจรรลจรฃฤฉรฑรตลฉ'], // Tilde
109109- ['AEIOUaeiou', 'ฤฤฤชลลชฤฤฤซลลซ'], // Macron
110110- ['AGUagu', 'ฤฤลฌฤฤลญ'], // Breve
111111- ['CEGIZcegz', 'ฤฤฤ ฤฐลปฤฤฤกลผ'], // Dot
112112- ['AEIOUYaeiouy', 'รรรรรลธรครซรฏรถรผรฟ'], // Umlaut or diรฆresis
113113- ['', ''],
114114- ['AUau', 'ร ลฎรฅลฏ'], // Ring
115115- ['CGKLNRSTcklnrst', 'รฤขฤถฤปล ลลลขรงฤทฤผลลลลฃ'], // Cedilla
116116- ['', ''],
117117- ['OUou', 'ลลฐลลฑ'], // Double Acute
118118- ['AEIUaeiu', 'ฤฤฤฎลฒฤ ฤฤฏลณ'], // Ogonek
119119- ['CDELNRSTZcdelnrstz', 'ฤฤฤฤฝลลล ลคลฝฤฤฤฤพลลลกลฅลพ'] // Caron
120120-];
121121-Stream.prototype.parseStringT61 = function (start, end, maxLength) {
122122- // warning: this code is not very well tested so far
123123- function merge(c, d) {
124124- var t = tableT61[c - 0xC0];
125125- var i = t[0].indexOf(String.fromCharCode(d));
126126- return (i < 0) ? '\0' : t[1].charAt(i);
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');
12784 }
128128- var s = "", c;
129129- for (var i = start; i < end; ++i) {
130130- c = this.get(i);
131131- if (c >= 0xA4 && c <= 0xBF)
132132- s += '$ยฅ#ยงยค\0\0ยซ\0\0\0\0ยฐยฑยฒยณรยตยถยทรท\0\0ยปยผยฝยพยฟ'.charAt(c - 0xA4);
133133- else if (c >= 0xE0 && c <= 0xFF)
134134- s += 'โฆรรยชฤฆ\0ฤฒฤฟลรลยบรลฆลลฤธรฆฤรฐฤงฤฑฤณลลรธลรรพลงล\0'.charAt(c - 0xE0);
135135- else if (c >= 0xC0 && c <= 0xCF)
136136- s += merge(c, this.get(++i));
137137- else // using ISO 8859-1 for characters undefined (or equal) in T.61
138138- s += String.fromCharCode(c);
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);
13993 }
140140- return { size: s.length, str: stringCut(s, maxLength) };
141141-};
142142-Stream.prototype.parseStringUTF = function (start, end, maxLength) {
143143- function ex(c) { // must be 10xxxxxx
144144- if ((c < 0x80) || (c >= 0xC0))
145145- throw new Error('Invalid UTF-8 continuation byte: ' + c);
146146- return (c & 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);
14798 }
148148- function surrogate(cp) {
149149- if (cp < 0x10000)
150150- throw new Error('UTF-8 overlong encoding, codepoint encoded in 4 bytes: ' + cp);
151151- // we could use String.fromCodePoint(cp) but let's be nice to older browsers and use surrogate pairs
152152- cp -= 0x10000;
153153- return String.fromCharCode((cp >> 10) + 0xD800, (cp & 0x3FF) + 0xDC00);
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;
154117 }
155155- var s = "";
156156- for (var i = start; i < end; ) {
157157- var c = this.get(i++);
158158- if (c < 0x80) // 0xxxxxxx (7 bit)
159159- s += String.fromCharCode(c);
160160- else if (c < 0xC0)
161161- throw new Error('Invalid UTF-8 starting byte: ' + c);
162162- else if (c < 0xE0) // 110xxxxx 10xxxxxx (11 bit)
163163- s += String.fromCharCode(((c & 0x1F) << 6) | ex(this.get(i++)));
164164- else if (c < 0xF0) // 1110xxxx 10xxxxxx 10xxxxxx (16 bit)
165165- s += String.fromCharCode(((c & 0x0F) << 12) | (ex(this.get(i++)) << 6) | ex(this.get(i++)));
166166- else if (c < 0xF8) // 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx (21 bit)
167167- s += surrogate(((c & 0x07) << 18) | (ex(this.get(i++)) << 12) | (ex(this.get(i++)) << 6) | ex(this.get(i++)));
168168- else
169169- throw new Error('Invalid UTF-8 starting byte (since 2003 it is restricted to 4 bytes): ' + c);
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;
170143 }
171171- return { size: s.length, str: stringCut(s, maxLength) };
172172-};
173173-Stream.prototype.parseStringBMP = function (start, end, maxLength) {
174174- var s = "", hi, lo;
175175- for (var i = start; i < end; ) {
176176- hi = this.get(i++);
177177- lo = this.get(i++);
178178- s += String.fromCharCode((hi << 8) | lo);
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;
179151 }
180180- return { size: s.length, str: stringCut(s, maxLength) };
181181-};
182182-Stream.prototype.parseTime = function (start, end, shortYear) {
183183- var s = this.parseStringISO(start, end).str,
184184- m = (shortYear ? reTimeS : reTimeL).exec(s);
185185- if (!m)
186186- return "Unrecognized time: " + s;
187187- if (shortYear) {
188188- // to avoid querying the timer, use the fixed range [1970, 2069]
189189- // it will conform with ITU X.400 [-10, +40] sliding window until 2030
190190- m[1] = +m[1];
191191- m[1] += (m[1] < 70) ? 2000 : 1900;
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) };
192157 }
193193- s = m[1] + "-" + m[2] + "-" + m[3] + " " + m[4];
194194- if (m[5]) {
195195- s += ":" + m[5];
196196- if (m[6]) {
197197- s += ":" + m[6];
198198- if (m[7])
199199- s += "." + m[7];
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);
200176 }
177177+ return { size: s.length, str: stringCut(s, maxLength) };
201178 }
202202- if (m[8]) {
203203- s += " UTC";
204204- if (m[9])
205205- s += m[9] + ":" + (m[10] || "00");
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) };
206209 }
207207- return s;
208208-};
209209-Stream.prototype.parseInteger = function (start, end) {
210210- var v = this.get(start),
211211- neg = (v > 127),
212212- pad = neg ? 255 : 0,
213213- len,
214214- s = '';
215215- // skip unuseful bits (not allowed in DER)
216216- while (v == pad && ++start < end)
217217- v = this.get(start);
218218- len = end - start;
219219- if (len === 0)
220220- return neg ? '-1' : '0';
221221- // show bit length of huge integers
222222- if (len > 4) {
223223- s = v;
224224- len <<= 3;
225225- while (((s ^ pad) & 0x80) == 0) {
226226- s <<= 1;
227227- --len;
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);
228216 }
229229- s = "(" + len + " bit)\n";
217217+ return { size: s.length, str: stringCut(s, maxLength) };
230218 }
231231- // decode the integer
232232- if (neg) v = v - 256;
233233- var n = new Int10(v);
234234- for (var i = start + 1; i < end; ++i)
235235- n.mulAdd(256, this.get(i));
236236- return s + n.toString();
237237-};
238238-Stream.prototype.parseBitString = function (start, end, maxLength) {
239239- var unusedBits = this.get(start);
240240- if (unusedBits > 7)
241241- throw 'Invalid BitString with unusedBits=' + unusedBits;
242242- var lenBit = ((end - start - 1) << 3) - unusedBits,
243243- s = "";
244244- for (var i = start + 1; i < end; ++i) {
245245- var b = this.get(i),
246246- skip = (i == end - 1) ? unusedBits : 0;
247247- for (var j = 7; j >= skip; --j)
248248- s += (b >> j) & 1 ? "1" : "0";
249249- if (s.length > maxLength)
250250- s = stringCut(s, maxLength);
251251- }
252252- return { size: lenBit, str: s };
253253-};
254254-function checkPrintable(s) {
255255- var i, v;
256256- for (i = 0; i < s.length; ++i) {
257257- v = s.charCodeAt(i);
258258- if (v < 32 && v != 9 && v != 10 && v != 13) // [\t\r\n] are (kinda) printable
259259- throw new Error('Unprintable character at index ' + i + ' (code ' + s.str.charCodeAt(i) + ")");
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';
241241+ if (m[9])
242242+ s += m[9] + ':' + (m[10] || '00');
243243+ }
244244+ return s;
260245 }
261261-}
262262-Stream.prototype.parseOctetString = function (start, end, maxLength) {
263263- var len = end - start,
264264- s;
265265- try {
266266- s = this.parseStringUTF(start, end, maxLength);
267267- checkPrintable(s.str);
268268- return { size: end - start, str: s.str };
269269- } catch (e) {
270270- // ignore
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';
267267+ }
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();
271274 }
272272- maxLength /= 2; // we work in bytes
273273- if (len > maxLength)
274274- end = start + maxLength;
275275- s = '';
276276- for (var i = start; i < end; ++i)
277277- s += this.hexByte(this.get(i));
278278- if (len > maxLength)
279279- s += ellipsis;
280280- return { size: len, str: s };
281281-};
282282-Stream.prototype.parseOID = function (start, end, maxLength, isRelative) {
283283- var s = '',
284284- n = new Int10(),
285285- bits = 0;
286286- for (var i = start; i < end; ++i) {
287287- var v = this.get(i);
288288- n.mulAdd(128, v & 0x7F);
289289- bits += 7;
290290- if (!(v & 0x80)) { // finished
291291- if (s === '') {
292292- n = n.simplify();
293293- if (isRelative) {
294294- s = (n instanceof Int10) ? n.toString() : "" + n;
295295- } else if (n instanceof Int10) {
296296- n.sub(80);
297297- s = "2." + n.toString();
298298- } else {
299299- var m = n < 80 ? n < 40 ? 0 : 1 : 2;
300300- s = m + "." + (n - m * 40);
301301- }
302302- } else
303303- s += "." + n.toString();
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';
304286 if (s.length > maxLength)
305305- return stringCut(s, maxLength);
306306- n = new Int10();
307307- bits = 0;
287287+ s = stringCut(s, maxLength);
308288 }
289289+ return { size: lenBit, str: s };
309290 }
310310- if (bits > 0)
311311- s += ".incomplete";
312312- if (typeof oids === 'object' && !isRelative) {
313313- var oid = oids[s];
314314- if (oid) {
315315- if (oid.d) s += "\n" + oid.d;
316316- if (oid.c) s += "\n" + oid.c;
317317- if (oid.w) s += "\n(warning!)";
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 (ignore) {
299299+ // ignore
318300 }
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;
309309+ return { size: len, str: s };
319310 }
320320- return s;
321321-};
322322-Stream.prototype.parseRelativeOID = function (start, end, maxLength) {
323323- return this.parseOID(start, end, maxLength, true);
324324-};
325325-326326-function ASN1(stream, header, length, tag, tagLen, sub) {
327327- if (!(tag instanceof ASN1Tag)) throw 'Invalid tag value.';
328328- this.stream = stream;
329329- this.header = header;
330330- this.length = length;
331331- this.tag = tag;
332332- this.tagLen = tagLen;
333333- this.sub = sub;
334334-}
335335-ASN1.prototype.typeName = function () {
336336- switch (this.tag.tagClass) {
337337- case 0: // universal
338338- switch (this.tag.tagNumber) {
339339- case 0x00: return "EOC";
340340- case 0x01: return "BOOLEAN";
341341- case 0x02: return "INTEGER";
342342- case 0x03: return "BIT_STRING";
343343- case 0x04: return "OCTET_STRING";
344344- case 0x05: return "NULL";
345345- case 0x06: return "OBJECT_IDENTIFIER";
346346- case 0x07: return "ObjectDescriptor";
347347- case 0x08: return "EXTERNAL";
348348- case 0x09: return "REAL";
349349- case 0x0A: return "ENUMERATED";
350350- case 0x0B: return "EMBEDDED_PDV";
351351- case 0x0C: return "UTF8String";
352352- case 0x0D: return "RELATIVE_OID";
353353- case 0x10: return "SEQUENCE";
354354- case 0x11: return "SET";
355355- case 0x12: return "NumericString";
356356- case 0x13: return "PrintableString"; // ASCII subset
357357- case 0x14: return "TeletexString"; // aka T61String
358358- case 0x15: return "VideotexString";
359359- case 0x16: return "IA5String"; // ASCII
360360- case 0x17: return "UTCTime";
361361- case 0x18: return "GeneralizedTime";
362362- case 0x19: return "GraphicString";
363363- case 0x1A: return "VisibleString"; // ASCII subset
364364- case 0x1B: return "GeneralString";
365365- case 0x1C: return "UniversalString";
366366- case 0x1E: return "BMPString";
311311+ parseOID(start, end, maxLength, isRelative) {
312312+ let s = '',
313313+ n = new Int10(),
314314+ 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+ }
338338+ }
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+ }
367348 }
368368- return "Universal_" + this.tag.tagNumber.toString();
369369- case 1: return "Application_" + this.tag.tagNumber.toString();
370370- case 2: return "[" + this.tag.tagNumber.toString() + "]"; // Context
371371- case 3: return "Private_" + this.tag.tagNumber.toString();
349349+ return s;
372350 }
373373-};
351351+ parseRelativeOID(start, end, maxLength) {
352352+ return this.parseOID(start, end, maxLength, true);
353353+ }
354354+}
355355+374356function recurse(el, parser, maxLength) {
375375- var avoidRecurse = true;
357357+ let avoidRecurse = true;
376358 if (el.tag.tagConstructed && el.sub) {
377359 avoidRecurse = false;
378360 el.sub.forEach(function (e1) {
···382364 }
383365 if (avoidRecurse)
384366 return el.stream[parser](el.posContent(), el.posContent() + Math.abs(el.length), maxLength);
385385- var d = { size: 0, str: '' };
367367+ let d = { size: 0, str: '' };
386368 el.sub.forEach(function (el) {
387387- var d1 = recurse(el, parser, maxLength - d.str.length);
369369+ let d1 = recurse(el, parser, maxLength - d.str.length);
388370 d.size += d1.size;
389371 d.str += d1.str;
390372 });
391373 return d;
392374}
393393-/** A string preview of the content (intended for humans). */
394394-ASN1.prototype.content = function (maxLength) {
395395- 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+ if (len === 0) return 'invalid length 0';
465465+ return (this.stream.get(content) === 0) ? 'false' : 'true';
466466+ case 0x02: // INTEGER
467467+ if (len === 0) return 'invalid length 0';
468468+ return this.stream.parseInteger(content, content + len);
469469+ case 0x03: { // BIT_STRING
470470+ let d = recurse(this, 'parseBitString', maxLength);
471471+ return '(' + d.size + ' bit)\n' + d.str;
472472+ }
473473+ case 0x04: { // OCTET_STRING
474474+ if (len === 0) return 'invalid length 0';
475475+ let d = recurse(this, 'parseOctetString', maxLength);
476476+ return '(' + d.size + ' byte)\n' + d.str;
477477+ }
478478+ //case 0x05: // NULL
479479+ case 0x06: // OBJECT_IDENTIFIER
480480+ return this.stream.parseOID(content, content + len, maxLength);
481481+ //case 0x07: // ObjectDescriptor
482482+ //case 0x08: // EXTERNAL
483483+ //case 0x09: // REAL
484484+ case 0x0A: // ENUMERATED
485485+ return this.stream.parseInteger(content, content + len);
486486+ //case 0x0B: // EMBEDDED_PDV
487487+ case 0x0D: // RELATIVE-OID
488488+ return this.stream.parseRelativeOID(content, content + len, maxLength);
489489+ case 0x10: // SEQUENCE
490490+ case 0x11: // SET
491491+ if (this.sub !== null)
492492+ return '(' + this.sub.length + ' elem)';
493493+ else
494494+ return '(no elem)';
495495+ case 0x0C: // UTF8String
496496+ return recurse(this, 'parseStringUTF', maxLength).str;
497497+ case 0x14: // TeletexString
498498+ return recurse(this, 'parseStringT61', maxLength).str;
499499+ case 0x12: // NumericString
500500+ case 0x13: // PrintableString
501501+ case 0x15: // VideotexString
502502+ case 0x16: // IA5String
503503+ case 0x1A: // VisibleString
504504+ case 0x1B: // GeneralString
505505+ //case 0x19: // GraphicString
506506+ //case 0x1C: // UniversalString
507507+ return recurse(this, 'parseStringISO', maxLength).str;
508508+ case 0x1E: // BMPString
509509+ return recurse(this, 'parseStringBMP', maxLength).str;
510510+ case 0x17: // UTCTime
511511+ case 0x18: // GeneralizedTime
512512+ return this.stream.parseTime(content, content + len, (this.tag.tagNumber == 0x17));
513513+ }
396514 return null;
397397- if (maxLength === undefined)
398398- maxLength = Infinity;
399399- var content = this.posContent(),
400400- len = Math.abs(this.length);
401401- if (!this.tag.isUniversal()) {
402402- if (this.sub !== null)
403403- return "(" + this.sub.length + " elem)";
404404- var d1 = this.stream.parseOctetString(content, content + len, maxLength);
405405- return "(" + d1.size + " byte)\n" + d1.str;
406515 }
407407- switch (this.tag.tagNumber) {
408408- case 0x01: // BOOLEAN
409409- return (this.stream.get(content) === 0) ? "false" : "true";
410410- case 0x02: // INTEGER
411411- return this.stream.parseInteger(content, content + len);
412412- case 0x03: // BIT_STRING
413413- var d = recurse(this, 'parseBitString', maxLength);
414414- return "(" + d.size + " bit)\n" + d.str;
415415- case 0x04: // OCTET_STRING
416416- d = recurse(this, 'parseOctetString', maxLength);
417417- return "(" + d.size + " byte)\n" + d.str;
418418- //case 0x05: // NULL
419419- case 0x06: // OBJECT_IDENTIFIER
420420- return this.stream.parseOID(content, content + len, maxLength);
421421- //case 0x07: // ObjectDescriptor
422422- //case 0x08: // EXTERNAL
423423- //case 0x09: // REAL
424424- case 0x0A: // ENUMERATED
425425- return this.stream.parseInteger(content, content + len);
426426- //case 0x0B: // EMBEDDED_PDV
427427- case 0x0D: // RELATIVE-OID
428428- return this.stream.parseRelativeOID(content, content + len, maxLength);
429429- case 0x10: // SEQUENCE
430430- case 0x11: // SET
431431- if (this.sub !== null)
432432- return "(" + this.sub.length + " elem)";
433433- else
434434- return "(no elem)";
435435- case 0x0C: // UTF8String
436436- return recurse(this, 'parseStringUTF', maxLength).str;
437437- case 0x14: // TeletexString
438438- return recurse(this, 'parseStringT61', maxLength).str;
439439- case 0x12: // NumericString
440440- case 0x13: // PrintableString
441441- case 0x15: // VideotexString
442442- case 0x16: // IA5String
443443- case 0x1A: // VisibleString
444444- case 0x1B: // GeneralString
445445- //case 0x19: // GraphicString
446446- //case 0x1C: // UniversalString
447447- return recurse(this, 'parseStringISO', maxLength).str;
448448- case 0x1E: // BMPString
449449- return recurse(this, 'parseStringBMP', maxLength).str;
450450- case 0x17: // UTCTime
451451- case 0x18: // GeneralizedTime
452452- return this.stream.parseTime(content, content + len, (this.tag.tagNumber == 0x17));
516516+ toString() {
517517+ return this.typeName() + '@' + this.stream.pos + '[header:' + this.header + ',length:' + this.length + ',sub:' + ((this.sub === null) ? 'null' : this.sub.length) + ']';
453518 }
454454- return null;
455455-};
456456-ASN1.prototype.toString = function () {
457457- return this.typeName() + "@" + this.stream.pos + "[header:" + this.header + ",length:" + this.length + ",sub:" + ((this.sub === null) ? 'null' : this.sub.length) + "]";
458458-};
459459-ASN1.prototype.toPrettyString = function (indent) {
460460- if (indent === undefined) indent = '';
461461- var s = indent + this.typeName() + " @" + this.stream.pos;
462462- if (this.length >= 0)
463463- s += "+";
464464- s += this.length;
465465- if (this.tag.tagConstructed)
466466- s += " (constructed)";
467467- else if ((this.tag.isUniversal() && ((this.tag.tagNumber == 0x03) || (this.tag.tagNumber == 0x04))) && (this.sub !== null))
468468- s += " (encapsulates)";
469469- var content = this.content();
470470- if (content)
471471- s += ": " + content.replace(/\n/g, '|');
472472- s += "\n";
473473- if (this.sub !== null) {
474474- indent += ' ';
475475- for (var i = 0, max = this.sub.length; i < max; ++i)
476476- s += this.sub[i].toPrettyString(indent);
519519+ toPrettyString(indent) {
520520+ if (indent === undefined) indent = '';
521521+ let s = indent;
522522+ if (this.def) {
523523+ if (this.def.id)
524524+ s += this.def.id + ' ';
525525+ if (this.def.name && this.def.name != this.typeName().replace(/_/g, ' '))
526526+ s+= this.def.name + ' ';
527527+ if (this.def.mismatch)
528528+ s += '[?] ';
529529+ }
530530+ s += this.typeName() + ' @' + this.stream.pos;
531531+ if (this.length >= 0)
532532+ s += '+';
533533+ s += this.length;
534534+ if (this.tag.tagConstructed)
535535+ s += ' (constructed)';
536536+ else if ((this.tag.isUniversal() && ((this.tag.tagNumber == 0x03) || (this.tag.tagNumber == 0x04))) && (this.sub !== null))
537537+ s += ' (encapsulates)';
538538+ let content = this.content();
539539+ if (content)
540540+ s += ': ' + content.replace(/\n/g, '|');
541541+ s += '\n';
542542+ if (this.sub !== null) {
543543+ indent += ' ';
544544+ for (let i = 0, max = this.sub.length; i < max; ++i)
545545+ s += this.sub[i].toPrettyString(indent);
546546+ }
547547+ return s;
477548 }
478478- return s;
479479-};
480480-ASN1.prototype.posStart = function () {
481481- return this.stream.pos;
482482-};
483483-ASN1.prototype.posContent = function () {
484484- return this.stream.pos + this.header;
485485-};
486486-ASN1.prototype.posEnd = function () {
487487- return this.stream.pos + this.header + Math.abs(this.length);
488488-};
489489-/** Position of the length. */
490490-ASN1.prototype.posLen = function() {
491491- return this.stream.pos + this.tagLen;
492492-};
493493-ASN1.prototype.toHexString = function () {
494494- return this.stream.hexDump(this.posStart(), this.posEnd(), true);
495495-};
496496-ASN1.prototype.toB64String = function () {
497497- return this.stream.b64Dump(this.posStart(), this.posEnd());
498498-};
499499-ASN1.decodeLength = function (stream) {
500500- var buf = stream.get(),
501501- len = buf & 0x7F;
502502- if (len == buf) // first bit was 0, short form
503503- return len;
504504- if (len === 0) // long form with length 0 is a special case
505505- return null; // undefined length
506506- if (len > 6) // no reason to use Int10, as it would be a huge buffer anyways
507507- throw "Length over 48 bits not supported at position " + (stream.pos - 1);
508508- buf = 0;
509509- for (var i = 0; i < len; ++i)
510510- buf = (buf * 256) + stream.get();
511511- return buf;
512512-};
513513-function ASN1Tag(stream) {
514514- var buf = stream.get();
515515- this.tagClass = buf >> 6;
516516- this.tagConstructed = ((buf & 0x20) !== 0);
517517- this.tagNumber = buf & 0x1F;
518518- if (this.tagNumber == 0x1F) { // long tag
519519- var n = new Int10();
520520- do {
521521- buf = stream.get();
522522- n.mulAdd(128, buf & 0x7F);
523523- } while (buf & 0x80);
524524- this.tagNumber = n.simplify();
549549+ posStart() {
550550+ return this.stream.pos;
525551 }
526526-}
527527-ASN1Tag.prototype.isUniversal = function () {
528528- return this.tagClass === 0x00;
529529-};
530530-ASN1Tag.prototype.isEOC = function () {
531531- return this.tagClass === 0x00 && this.tagNumber === 0x00;
532532-};
533533-ASN1.decode = function (stream, offset) {
534534- if (!(stream instanceof Stream))
535535- stream = new Stream(stream, offset || 0);
536536- var streamStart = new Stream(stream),
537537- tag = new ASN1Tag(stream),
538538- tagLen = stream.pos - streamStart.pos,
539539- len = ASN1.decodeLength(stream),
540540- start = stream.pos,
541541- header = start - streamStart.pos,
542542- sub = null,
543543- getSub = function () {
544544- sub = [];
545545- if (len !== null) {
546546- // definite length
547547- var end = start + len;
548548- if (end > stream.enc.length)
549549- throw 'Container at offset ' + start + ' has a length of ' + len + ', which is past the end of the stream';
550550- while (stream.pos < end)
551551- sub[sub.length] = ASN1.decode(stream);
552552- if (stream.pos != end)
553553- throw 'Content size is not correct for container at offset ' + start;
554554- } else {
555555- // undefined length
556556- try {
557557- for (;;) {
558558- var s = ASN1.decode(stream);
559559- if (s.tag.isEOC())
560560- break;
561561- sub[sub.length] = s;
552552+ posContent() {
553553+ return this.stream.pos + this.header;
554554+ }
555555+ posEnd() {
556556+ return this.stream.pos + this.header + Math.abs(this.length);
557557+ }
558558+ /** Position of the length. */
559559+ posLen() {
560560+ return this.stream.pos + this.tagLen;
561561+ }
562562+ /** Hexadecimal dump of the node.
563563+ * @param type 'raw', 'byte' or 'dump' */
564564+ toHexString(type = 'raw') {
565565+ return this.stream.hexDump(this.posStart(), this.posEnd(), type);
566566+ }
567567+ /** Base64url dump of the node (according to RFC 4648 section 5).
568568+ * @param {string} type 'url' (default, section 5 without padding) or 'std' (section 4 with padding)
569569+ */
570570+ toB64String(type = 'url') {
571571+ return this.stream.b64Dump(this.posStart(), this.posEnd(), type);
572572+ }
573573+ static decodeLength(stream) {
574574+ let buf = stream.get(),
575575+ len = buf & 0x7F;
576576+ if (len == buf) // first bit was 0, short form
577577+ return len;
578578+ if (len === 0) // long form with length 0 is a special case
579579+ return null; // undefined length
580580+ if (len > 6) // no reason to use Int10, as it would be a huge buffer anyways
581581+ throw new Error('Length over 48 bits not supported at position ' + (stream.pos - 1));
582582+ buf = 0;
583583+ for (let i = 0; i < len; ++i)
584584+ buf = (buf * 256) + stream.get();
585585+ return buf;
586586+ }
587587+ static decode(stream, offset, type = ASN1) {
588588+ if (!(type == ASN1 || type.prototype instanceof ASN1))
589589+ throw new Error('Must pass a class that extends ASN1');
590590+ if (!(stream instanceof Stream))
591591+ stream = new Stream(stream, offset || 0);
592592+ let streamStart = new Stream(stream),
593593+ tag = new ASN1Tag(stream),
594594+ tagLen = stream.pos - streamStart.pos,
595595+ len = ASN1.decodeLength(stream),
596596+ start = stream.pos,
597597+ header = start - streamStart.pos,
598598+ sub = null,
599599+ getSub = function () {
600600+ sub = [];
601601+ if (len !== null) {
602602+ // definite length
603603+ let end = start + len;
604604+ if (end > stream.enc.length)
605605+ throw new Error('Container at offset ' + start + ' has a length of ' + len + ', which is past the end of the stream');
606606+ while (stream.pos < end)
607607+ sub[sub.length] = type.decode(stream);
608608+ if (stream.pos != end)
609609+ throw new Error('Content size is not correct for container at offset ' + start);
610610+ } else {
611611+ // undefined length
612612+ try {
613613+ for (;;) {
614614+ let s = type.decode(stream);
615615+ if (s.tag.isEOC())
616616+ break;
617617+ sub[sub.length] = s;
618618+ }
619619+ len = start - stream.pos; // undefined lengths are represented as negative values
620620+ } catch (e) {
621621+ throw new Error('Exception while decoding undefined length content at offset ' + start + ': ' + e);
622622+ }
623623+ }
624624+ };
625625+ if (tag.tagConstructed) {
626626+ // must have valid content
627627+ getSub();
628628+ } else if (tag.isUniversal() && ((tag.tagNumber == 0x03) || (tag.tagNumber == 0x04))) {
629629+ // sometimes BitString and OctetString are used to encapsulate ASN.1
630630+ try {
631631+ if (tag.tagNumber == 0x03)
632632+ if (stream.get() != 0)
633633+ throw new Error('BIT STRINGs with unused bits cannot encapsulate.');
634634+ getSub();
635635+ for (let s of sub) {
636636+ if (s.tag.isEOC())
637637+ throw new Error('EOC is not supposed to be actual content.');
638638+ try {
639639+ s.content();
640640+ } catch (e) {
641641+ throw new Error('Unable to parse content: ' + e);
562642 }
563563- len = start - stream.pos; // undefined lengths are represented as negative values
564564- } catch (e) {
565565- throw 'Exception while decoding undefined length content at offset ' + start + ': ' + e;
566643 }
644644+ } catch (ignore) {
645645+ // but silently ignore when they don't
646646+ sub = null;
647647+ //DEBUG console.log('Could not decode structure at ' + start + ':', e);
567648 }
568568- };
569569- if (tag.tagConstructed) {
570570- // must have valid content
571571- getSub();
572572- } else if (tag.isUniversal() && ((tag.tagNumber == 0x03) || (tag.tagNumber == 0x04))) {
573573- // sometimes BitString and OctetString are used to encapsulate ASN.1
574574- try {
575575- if (tag.tagNumber == 0x03)
576576- if (stream.get() != 0)
577577- throw "BIT STRINGs with unused bits cannot encapsulate.";
578578- getSub();
579579- for (var i = 0; i < sub.length; ++i)
580580- if (sub[i].tag.isEOC())
581581- throw 'EOC is not supposed to be actual content.';
582582- } catch (e) {
583583- // but silently ignore when they don't
584584- sub = null;
585585- //DEBUG console.log('Could not decode structure at ' + start + ':', e);
649649+ }
650650+ if (sub === null) {
651651+ if (len === null)
652652+ throw new Error("We can't skip over an invalid tag with undefined length at offset " + start);
653653+ stream.pos = start + Math.abs(len);
586654 }
655655+ return new type(streamStart, header, len, tag, tagLen, sub);
587656 }
588588- if (sub === null) {
589589- if (len === null)
590590- throw "We can't skip over an invalid tag with undefined length at offset " + start;
591591- stream.pos = start + Math.abs(len);
592592- }
593593- return new ASN1(streamStart, header, len, tag, tagLen, sub);
594594-};
595657596596-return ASN1;
597597-598598-});
658658+}
+79-82
base64.js
···11// Base64 JavaScript decoder
22-// Copyright (c) 2008-2022 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+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+====
+45
examples/crl-rfc5280.b64.dump
···11+CertificateList SEQUENCE @0+352 (constructed): (3 elem)
22+ tbsCertList TBSCertList SEQUENCE @4+202 (constructed): (7 elem)
33+ version Version INTEGER @7+1: 1
44+ signature AlgorithmIdentifier SEQUENCE @10+13 (constructed): (2 elem)
55+ algorithm OBJECT_IDENTIFIER @12+9: 1.2.840.113549.1.1.5|sha1WithRSAEncryption|PKCS #1
66+ parameters ANY NULL @23+0
77+ issuer rdnSequence Name SEQUENCE @25+67 (constructed): (3 elem)
88+ RelativeDistinguishedName SET @27+19 (constructed): (1 elem)
99+ AttributeTypeAndValue SEQUENCE @29+17 (constructed): (2 elem)
1010+ type AttributeType OBJECT_IDENTIFIER @31+10: 0.9.2342.19200300.100.1.25|domainComponent|Men are from Mars, this OID is from Pluto
1111+ value AttributeValue [?] IA5String @43+3: com
1212+ RelativeDistinguishedName SET @48+23 (constructed): (1 elem)
1313+ AttributeTypeAndValue SEQUENCE @50+21 (constructed): (2 elem)
1414+ type AttributeType OBJECT_IDENTIFIER @52+10: 0.9.2342.19200300.100.1.25|domainComponent|Men are from Mars, this OID is from Pluto
1515+ value AttributeValue [?] IA5String @64+7: example
1616+ RelativeDistinguishedName SET @73+19 (constructed): (1 elem)
1717+ AttributeTypeAndValue SEQUENCE @75+17 (constructed): (2 elem)
1818+ type AttributeType OBJECT_IDENTIFIER @77+3: 2.5.4.3|commonName|X.520 DN component
1919+ value AttributeValue [?] PrintableString @82+10: Example CA
2020+ thisUpdate utcTime Time UTCTime @94+13: 2005-02-05 12:00:00 UTC
2121+ nextUpdate utcTime Time UTCTime @109+13: 2005-02-06 12:00:00 UTC
2222+ revokedCertificates SEQUENCE @124+34 (constructed): (1 elem)
2323+ SEQUENCE @126+32 (constructed): (3 elem)
2424+ userCertificate CertificateSerialNumber INTEGER @128+1: 18
2525+ revocationDate utcTime Time UTCTime @131+13: 2004-11-19 15:57:03 UTC
2626+ crlEntryExtensions Extensions SEQUENCE @146+12 (constructed): (1 elem)
2727+ Extension SEQUENCE @148+10 (constructed): (2 elem)
2828+ extnID OBJECT_IDENTIFIER @150+3: 2.5.29.21|cRLReason|X.509 extension
2929+ extnValue OCTET_STRING @155+3 (encapsulates): (3 byte)|0A0101
3030+ ENUMERATED @157+1: 1
3131+ crlExtensions [0] @160+47 (constructed): (1 elem)
3232+ Extensions SEQUENCE @162+45 (constructed): (2 elem)
3333+ Extension SEQUENCE @164+31 (constructed): (2 elem)
3434+ extnID OBJECT_IDENTIFIER @166+3: 2.5.29.35|authorityKeyIdentifier|X.509 extension
3535+ extnValue OCTET_STRING @171+24 (encapsulates): (24 byte)|301680140868AF8533C8394A7AF882938E706A4A20842C32
3636+ SEQUENCE @173+22 (constructed): (1 elem)
3737+ [0] @175+20: (20 byte)|0868AF8533C8394A7AF882938E706A4A20842C32
3838+ Extension SEQUENCE @197+10 (constructed): (2 elem)
3939+ extnID OBJECT_IDENTIFIER @199+3: 2.5.29.20|cRLNumber|X.509 extension
4040+ extnValue OCTET_STRING @204+3 (encapsulates): (3 byte)|02010C
4141+ INTEGER @206+1: 12
4242+ signatureAlgorithm AlgorithmIdentifier SEQUENCE @209+13 (constructed): (2 elem)
4343+ algorithm OBJECT_IDENTIFIER @211+9: 1.2.840.113549.1.1.5|sha1WithRSAEncryption|PKCS #1
4444+ parameters ANY NULL @222+0
4545+ signature BIT_STRING @224+129: (1024 bit)|0010001011011100000110000111110111110111000010001100111011001100011101011101000011010000011010101001101110101101000100001111010001110110001000111011010010000001011011101011010101101101101111100000111011111011000101010001010001101100110010000001011101101101000111111110111010010000000101111010001001101111011000001110010010111101101010101000110001010101110111101000111010000100011011111001001011111000100111110001000000010010001001111010111101001010110101000010111110000101111000100011011001000100011111011010101010100011010011000010010100111000000101011111111100000000111111010011111001111110111011100011110100100110000100101110101111011000111001110010101101100010111000100010101111000011010001101000000011101111011110001000001011010001000101011100011011010000100111000111001001101010110010111100111001111010111011010110011110011001100010110110111001110000100000010111110101000011010000100111010011000001101001101010111111000001010101010001011110100010001100110100110011010110000001101001100000101011101001001111110000101110
···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-2022 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 (ignore) {
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);
8484- } catch (e) {
125125+ } catch (ignore) {
85126 text(tree, 'Cannot decode file.');
86127 dump.innerHTML = '';
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 = '';
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+ },
97158};
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();
113113-};
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-2022 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+}
···1010fi
1111cat dumpasn1.cfg | \
1212tr -d '\r' | \
1313-awk -v url="$URL" '
1313+awk -v apos="'" -v q='"' -v url="$URL" '
1414 function clean() {
1515 oid = "";
1616 comment = "";
···1919 }
2020 BEGIN {
2121 FS = "= *";
2222- apos = sprintf("%c", 39);
2322 clean();
2423 print "// Converted from: " url;
2524 print "// which is made by Peter Gutmann and whose license states:";
2625 print "// You can use this code in whatever way you want,";
2726 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 {";
2727+ print "export const oids = {";
3428 }
3529 /^OID/ { oid = $2; }
3630 /^Comment/ { comment = $2; }
···4943 }
5044 }
5145 END {
5252- print "\"END\": \"\""
5353- print "};});"
4646+ print "};"
5447 }
5548' >oids.js
5649echo Conversion completed.
+15-3
updateRFC.sh
···11#/bin/sh
22+RFCs="5280 5208 3369 3161 2986 4211 4210 8017 4511"
23downloadRFC() {
34 URL="https://www.ietf.org/rfc/rfc$1.txt"
45 if [ -x /usr/bin/fetch ]; then
···1011 exit 1
1112 fi
1213}
1313-echo '{}' > rfcasn1.json # start from scratch
1414+echo '{}' > rfcdef.json # start from scratch
1415mkdir -p rfc
1516cd rfc
1616-for n in 5280 3369 3161; do
1717+for n in $RFCs; do
1718 downloadRFC $n
1818- ../parseRFC.js rfc$n.txt ../rfcasn1.json
1919+ ../parseRFC.js rfc$n.txt ../rfcdef.json
1920done
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
2032echo Conversion completed.