···1-ASN.1 JavaScript decoder Copyright (c) 2008-2021 Lapo Luchini <lapo@lapo.it>
0023Permission to use, copy, modify, and/or distribute this software for any
4purpose with or without fee is hereby granted, provided that the above
···11WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
13OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14-
···1+ISC License
2+3+Copyright (c) 2008-2025 Lapo Luchini <lapo@lapo.it>
45Permission to use, copy, modify, and/or distribute this software for any
6purpose with or without fee is hereby granted, provided that the above
···13WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
0
+54-21
README.md
···34asn1js is a JavaScript generic ASN.1 parser/decoder that can decode any valid ASN.1 DER or BER structures.
56-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).
78-Usage with `npm` / `yarn`
9--------------------------
1011This package can be installed with either npm or yarn via the following commands:
1213```sh
14npm install @lapo/asn1js
15-# or with yarn
016yarn add @lapo/asn1js
17```
1819-Assuming a standard javascript bundler is setup you can import it like so:
2021```js
22-const ASN1 = require('@lapo/asn1js');
23-// or with ES modules
24-import ASN1 from '@lapo/asn1js';
25```
2627A submodule of this package can also be imported:
2829```js
30-const Hex = require('@lapo/asn1js/hex');
31-// or with ES modules
32-import Hex from '@lapo/asn1js/hex';
000000000000000000033```
3435-Usage with RequireJS
36--------------------
3738-Can be [tested on JSFiddle](https://jsfiddle.net/lapo/tmdq35ug/).
3940```html
41-<script type="text/javascript" src="https://unpkg.com/requirejs/require.js"></script>
42<script>
43-require([
44- 'https://unpkg.com/@lapo/asn1js/asn1.js',
45- 'https://unpkg.com/@lapo/asn1js/hex.js'
46-], function(ASN1, Hex) {
47- document.body.innerText = ASN1.decode(Hex.decode('06032B6570')).content();
48-});
49</script>
50```
510000000000000052ISC license
53-----------
5455-ASN.1 JavaScript decoder Copyright (c) 2008-2020 Lapo Luchini <lapo@lapo.it>
5657Permission 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.
58···65- BMPString support added by [Felipe Gasper](https://github.com/FGasper)
66- extended tag support added by [Pรฉter Budai](https://www.peterbudai.eu/)
67- patches by [Gergely Nagy](https://github.com/ngg)
0006869links
70-----
7172- [official website](https://lapo.it/asn1js/)
073- [InDefero tracker](http://idf.lapo.it/p/asn1js/)
74- [GitHub mirror](https://github.com/lapo-luchini/asn1js)
75- [Ohloh code stats](https://www.openhub.net/p/asn1js)
···34asn1js is a JavaScript generic ASN.1 parser/decoder that can decode any valid ASN.1 DER or BER structures.
56+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`.
78+Usage with `nodejs`
9+-------------------
1011This package can be installed with either npm or yarn via the following commands:
1213```sh
14npm install @lapo/asn1js
15+# or other tools
16+pnpm install @lapo/asn1js
17yarn add @lapo/asn1js
18```
1920+You can import the classes like this:
2122```js
23+import { ASN1 } from '@lapo/asn1js';
0024```
2526A submodule of this package can also be imported:
2728```js
29+import { Hex } from '@lapo/asn1js/hex.js';
30+```
31+32+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`):
33+34+```js
35+const
36+ { ASN1 } = require('@lapo/asn1js'),
37+ { Hex } = require('@lapo/asn1js/hex.js');
38+console.log(ASN1.decode(Hex.decode('06032B6570')).content());
39+```
40+41+On older NodeJS you instead need to use async `import`:
42+43+```js
44+async function main() {
45+ const
46+ { ASN1 } = await import('@lapo/asn1js'),
47+ { Hex } = await import('@lapo/asn1js/hex.js');
48+ console.log(ASN1.decode(Hex.decode('06032B6570')).content());
49+}
50+main();
51```
5253+Usage on the web
54--------------------
5556+Can be [tested on JSFiddle](https://jsfiddle.net/lapo/y6t2wo7q/).
5758```html
059<script>
60+import { ASN1 } from 'https://unpkg.com/@lapo/asn1js@2.0.0/asn1.js';
61+import { Hex } from 'https://unpkg.com/@lapo/asn1js@2.0.0/hex.js';
62+63+document.body.innerText = ASN1.decode(Hex.decode('06032B6570')).content();
0064</script>
65```
6667+Local usage
68+--------------------
69+70+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.
71+72+Usage from CLI
73+--------------------
74+75+You can dump an ASN.1 structure from the command line using the following command (no need to even install it):
76+77+```sh
78+npx @lapo/asn1js ed25519.cer
79+```
80+81ISC license
82-----------
8384+ASN.1 JavaScript decoder Copyright (c) 2008-2025 Lapo Luchini <lapo@lapo.it>
8586Permission 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.
87···94- BMPString support added by [Felipe Gasper](https://github.com/FGasper)
95- extended tag support added by [Pรฉter Budai](https://www.peterbudai.eu/)
96- patches by [Gergely Nagy](https://github.com/ngg)
97+- Relative OID support added by [Mistial Developer](https://github.com/mistial-dev)
98+- dark mode and other UI improvements by [Oliver Burgmaier](https://github.com/olibu/)
99+- patches by [Nicolai Sรธborg](https://github.com/NicolaiSoeborg)
100101links
102-----
103104- [official website](https://lapo.it/asn1js/)
105+- [dedicated domain](https://asn1js.eu/)
106- [InDefero tracker](http://idf.lapo.it/p/asn1js/)
107- [GitHub mirror](https://github.com/lapo-luchini/asn1js)
108- [Ohloh code stats](https://www.openhub.net/p/asn1js)
+580-475
asn1.js
···1// ASN.1 JavaScript decoder
2-// Copyright (c) 2008-2021 Lapo Luchini <lapo@lapo.it>
34// Permission to use, copy, modify, and/or distribute this software for any
5// purpose with or without fee is hereby granted, provided that the above
6// copyright notice and this permission notice appear in all copies.
7-//
8// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
···13// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1516-(typeof define != 'undefined' ? define : function (factory) { 'use strict';
17- if (typeof module == 'object') module.exports = factory(function (name) { return require(name); });
18- else window.asn1 = factory(function (name) { return window[name.substring(2)]; });
19-})(function (require) {
20-"use strict";
2122-var Int10 = require('./int10'),
23- oids = require('./oids'),
24- ellipsis = "\u2026",
25- 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)?)?$/,
26- 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)?)?$/;
000000000000000000002728function stringCut(str, len) {
29 if (str.length > len)
···31 return str;
32}
3334-function Stream(enc, pos) {
35- if (enc instanceof Stream) {
36- this.enc = enc.enc;
37- this.pos = enc.pos;
38- } else {
39- // enc should be an array or a binary string
40- this.enc = enc;
41- this.pos = pos;
42 }
43}
44-Stream.prototype.get = function (pos) {
45- if (pos === undefined)
46- pos = this.pos++;
47- if (pos >= this.enc.length)
48- throw 'Requesting byte offset ' + pos + ' on a stream of length ' + this.enc.length;
49- return (typeof this.enc == "string") ? this.enc.charCodeAt(pos) : this.enc[pos];
50-};
51-Stream.prototype.hexDigits = "0123456789ABCDEF";
52-Stream.prototype.hexByte = function (b) {
53- return this.hexDigits.charAt((b >> 4) & 0xF) + this.hexDigits.charAt(b & 0xF);
54-};
55-Stream.prototype.hexDump = function (start, end, raw) {
56- var s = "";
57- for (var i = start; i < end; ++i) {
58- s += this.hexByte(this.get(i));
59- if (raw !== true)
60- switch (i & 0xF) {
61- case 0x7: s += " "; break;
62- case 0xF: s += "\n"; break;
63- default: s += " ";
64- }
000065 }
66- return s;
67-};
68-var b64Safe = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
69-Stream.prototype.b64Dump = function (start, end) {
70- var extra = (end - start) % 3,
71- s = '',
72- i, c;
73- for (i = start; i + 2 < end; i += 3) {
74- c = this.get(i) << 16 | this.get(i + 1) << 8 | this.get(i + 2);
75- s += b64Safe.charAt(c >> 18 & 0x3F);
76- s += b64Safe.charAt(c >> 12 & 0x3F);
77- s += b64Safe.charAt(c >> 6 & 0x3F);
78- s += b64Safe.charAt(c & 0x3F);
79 }
80- if (extra > 0) {
81- c = this.get(i) << 16;
82- if (extra > 1) c |= this.get(i + 1) << 8;
83- s += b64Safe.charAt(c >> 18 & 0x3F);
84- s += b64Safe.charAt(c >> 12 & 0x3F);
85- if (extra == 2) s += b64Safe.charAt(c >> 6 & 0x3F);
86 }
87- return s;
88-};
89-Stream.prototype.isASCII = function (start, end) {
90- for (var i = start; i < end; ++i) {
91- var c = this.get(i);
92- if (c < 32 || c > 176)
93- return false;
0000000000094 }
95- return true;
96-};
97-Stream.prototype.parseStringISO = function (start, end) {
98- var s = "";
99- for (var i = start; i < end; ++i)
100- s += String.fromCharCode(this.get(i));
101- return s;
102-};
103-Stream.prototype.parseStringUTF = function (start, end) {
104- function ex(c) { // must be 10xxxxxx
105- if ((c < 0x80) || (c >= 0xC0))
106- throw new Error('Invalid UTF-8 continuation byte: ' + c);
107- return (c & 0x3F);
000000000000108 }
109- function surrogate(cp) {
110- if (cp < 0x10000)
111- throw new Error('UTF-8 overlong encoding, codepoint encoded in 4 bytes: ' + cp);
112- // we could use String.fromCodePoint(cp) but let's be nice to older browsers and use surrogate pairs
113- cp -= 0x10000;
114- return String.fromCharCode((cp >> 10) + 0xD800, (cp & 0x3FF) + 0xDC00);
0115 }
116- var s = "";
117- for (var i = start; i < end; ) {
118- var c = this.get(i++);
119- if (c < 0x80) // 0xxxxxxx (7 bit)
120- s += String.fromCharCode(c);
121- else if (c < 0xC0)
122- throw new Error('Invalid UTF-8 starting byte: ' + c);
123- else if (c < 0xE0) // 110xxxxx 10xxxxxx (11 bit)
124- s += String.fromCharCode(((c & 0x1F) << 6) | ex(this.get(i++)));
125- else if (c < 0xF0) // 1110xxxx 10xxxxxx 10xxxxxx (16 bit)
126- s += String.fromCharCode(((c & 0x0F) << 12) | (ex(this.get(i++)) << 6) | ex(this.get(i++)));
127- else if (c < 0xF8) // 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx (21 bit)
128- s += surrogate(((c & 0x07) << 18) | (ex(this.get(i++)) << 12) | (ex(this.get(i++)) << 6) | ex(this.get(i++)));
129- else
130- throw new Error('Invalid UTF-8 starting byte (since 2003 it is restricted to 4 bytes): ' + c);
131 }
132- return s;
133-};
134-Stream.prototype.parseStringBMP = function (start, end) {
135- var str = "", hi, lo;
136- for (var i = start; i < end; ) {
137- hi = this.get(i++);
138- lo = this.get(i++);
139- str += String.fromCharCode((hi << 8) | lo);
000000000000140 }
141- return str;
142-};
143-Stream.prototype.parseTime = function (start, end, shortYear) {
144- var s = this.parseStringISO(start, end),
145- m = (shortYear ? reTimeS : reTimeL).exec(s);
146- if (!m)
147- return "Unrecognized time: " + s;
148- if (shortYear) {
149- // to avoid querying the timer, use the fixed range [1970, 2069]
150- // it will conform with ITU X.400 [-10, +40] sliding window until 2030
151- m[1] = +m[1];
152- m[1] += (m[1] < 70) ? 2000 : 1900;
000000000000000000153 }
154- s = m[1] + "-" + m[2] + "-" + m[3] + " " + m[4];
155- if (m[5]) {
156- s += ":" + m[5];
157- if (m[6]) {
158- s += ":" + m[6];
159- if (m[7])
160- s += "." + m[7];
161 }
0162 }
163- if (m[8]) {
164- s += " UTC";
165- if (m[8] != 'Z') {
166- s += m[8];
000000000000000000167 if (m[9])
168- s += ":" + m[9];
169 }
0170 }
171- return s;
172-};
173-Stream.prototype.parseInteger = function (start, end) {
174- var v = this.get(start),
175- neg = (v > 127),
176- pad = neg ? 255 : 0,
177- len,
178- s = '';
179- // skip unuseful bits (not allowed in DER)
180- while (v == pad && ++start < end)
181- v = this.get(start);
182- len = end - start;
183- if (len === 0)
184- return neg ? '-1' : '0';
185- // show bit length of huge integers
186- if (len > 4) {
187- s = v;
188- len <<= 3;
189- while (((s ^ pad) & 0x80) == 0) {
190- s <<= 1;
191- --len;
192 }
193- s = "(" + len + " bit)\n";
00000194 }
195- // decode the integer
196- if (neg) v = v - 256;
197- var n = new Int10(v);
198- for (var i = start + 1; i < end; ++i)
199- n.mulAdd(256, this.get(i));
200- return s + n.toString();
201-};
202-Stream.prototype.parseBitString = function (start, end, maxLength) {
203- var unusedBits = this.get(start);
204- if (unusedBits > 7)
205- throw 'Invalid BitString with unusedBits=' + unusedBits;
206- var lenBit = ((end - start - 1) << 3) - unusedBits,
207- s = "";
208- for (var i = start + 1; i < end; ++i) {
209- var b = this.get(i),
210- skip = (i == end - 1) ? unusedBits : 0;
211- for (var j = 7; j >= skip; --j)
212- s += (b >> j) & 1 ? "1" : "0";
213- if (s.length > maxLength)
214- s = stringCut(s, maxLength);
215 }
216- return { size: lenBit, str: s };
217-};
218-Stream.prototype.parseOctetString = function (start, end, maxLength) {
219- var len = end - start,
220- s;
221- try {
222- s = this.parseStringUTF(start, end);
223- var v;
224- for (i = 0; i < s.length; ++i) {
225- v = s.charCodeAt(i);
226- if (v < 32 && v != 9 && v != 10 && v != 13) // [\t\r\n] are (kinda) printable
227- throw new Error('Unprintable character at index ' + i + ' (code ' + s.charCodeAt(i) + ")");
228 }
00000000229 return { size: len, str: s };
230- } catch (e) {
231- // ignore
232 }
233- maxLength /= 2; // we work in bytes
234- if (len > maxLength)
235- end = start + maxLength;
236- s = '';
237- for (var i = start; i < end; ++i)
238- s += this.hexByte(this.get(i));
239- if (len > maxLength)
240- s += ellipsis;
241- return { size: len, str: s };
242-};
243-Stream.prototype.parseOID = function (start, end, maxLength) {
244- var s = '',
245- n = new Int10(),
246- bits = 0;
247- for (var i = start; i < end; ++i) {
248- var v = this.get(i);
249- n.mulAdd(128, v & 0x7F);
250- bits += 7;
251- if (!(v & 0x80)) { // finished
252- if (s === '') {
253- n = n.simplify();
254- if (n instanceof Int10) {
255- n.sub(80);
256- s = "2." + n.toString();
257- } else {
258- var m = n < 80 ? n < 40 ? 0 : 1 : 2;
259- s = m + "." + (n - m * 40);
260- }
261- } else
262- s += "." + n.toString();
263- if (s.length > maxLength)
264- return stringCut(s, maxLength);
265- n = new Int10();
266 bits = 0;
00000000000000000000000267 }
00000000000268 }
269- if (bits > 0)
270- s += ".incomplete";
271- if (typeof oids === 'object') {
272- var oid = oids[s];
273- if (oid) {
274- if (oid.d) s += "\n" + oid.d;
275- if (oid.c) s += "\n" + oid.c;
276- if (oid.w) s += "\n(warning!)";
277- }
278 }
279- return s;
280-};
281-282-function ASN1(stream, header, length, tag, tagLen, sub) {
283- if (!(tag instanceof ASN1Tag)) throw 'Invalid tag value.';
284- this.stream = stream;
285- this.header = header;
286- this.length = length;
287- this.tag = tag;
288- this.tagLen = tagLen;
289- this.sub = sub;
290}
291-ASN1.prototype.typeName = function () {
292- switch (this.tag.tagClass) {
293- case 0: // universal
294- switch (this.tag.tagNumber) {
295- case 0x00: return "EOC";
296- case 0x01: return "BOOLEAN";
297- case 0x02: return "INTEGER";
298- case 0x03: return "BIT_STRING";
299- case 0x04: return "OCTET_STRING";
300- case 0x05: return "NULL";
301- case 0x06: return "OBJECT_IDENTIFIER";
302- case 0x07: return "ObjectDescriptor";
303- case 0x08: return "EXTERNAL";
304- case 0x09: return "REAL";
305- case 0x0A: return "ENUMERATED";
306- case 0x0B: return "EMBEDDED_PDV";
307- case 0x0C: return "UTF8String";
308- case 0x10: return "SEQUENCE";
309- case 0x11: return "SET";
310- case 0x12: return "NumericString";
311- case 0x13: return "PrintableString"; // ASCII subset
312- case 0x14: return "TeletexString"; // aka T61String
313- case 0x15: return "VideotexString";
314- case 0x16: return "IA5String"; // ASCII
315- case 0x17: return "UTCTime";
316- case 0x18: return "GeneralizedTime";
317- case 0x19: return "GraphicString";
318- case 0x1A: return "VisibleString"; // ASCII subset
319- case 0x1B: return "GeneralString";
320- case 0x1C: return "UniversalString";
321- case 0x1E: return "BMPString";
322- }
323- return "Universal_" + this.tag.tagNumber.toString();
324- case 1: return "Application_" + this.tag.tagNumber.toString();
325- case 2: return "[" + this.tag.tagNumber.toString() + "]"; // Context
326- case 3: return "Private_" + this.tag.tagNumber.toString();
327- }
328-};
329function recurse(el, parser, maxLength) {
330- var avoidRecurse = true;
331 if (el.tag.tagConstructed && el.sub) {
332 avoidRecurse = false;
333 el.sub.forEach(function (e1) {
···337 }
338 if (avoidRecurse)
339 return el.stream[parser](el.posContent(), el.posContent() + Math.abs(el.length), maxLength);
340- var d = { size: 0, str: '' };
341 el.sub.forEach(function (el) {
342- var d1 = recurse(el, parser, maxLength - d.str.length);
343 d.size += d1.size;
344 d.str += d1.str;
345 });
346 return d;
347}
348-/** A string preview of the content (intended for humans). */
349-ASN1.prototype.content = function (maxLength) {
350- if (this.tag === undefined)
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000351 return null;
352- if (maxLength === undefined)
353- maxLength = Infinity;
354- var content = this.posContent(),
355- len = Math.abs(this.length);
356- if (!this.tag.isUniversal()) {
357- if (this.sub !== null)
358- return "(" + this.sub.length + " elem)";
359- var d1 = this.stream.parseOctetString(content, content + len, maxLength);
360- return "(" + d1.size + " byte)\n" + d1.str;
361 }
362- switch (this.tag.tagNumber) {
363- case 0x01: // BOOLEAN
364- return (this.stream.get(content) === 0) ? "false" : "true";
365- case 0x02: // INTEGER
366- return this.stream.parseInteger(content, content + len);
367- case 0x03: // BIT_STRING
368- var d = recurse(this, 'parseBitString', maxLength);
369- return "(" + d.size + " bit)\n" + d.str;
370- case 0x04: // OCTET_STRING
371- d = recurse(this, 'parseOctetString', maxLength);
372- return "(" + d.size + " byte)\n" + d.str;
373- //case 0x05: // NULL
374- case 0x06: // OBJECT_IDENTIFIER
375- return this.stream.parseOID(content, content + len, maxLength);
376- //case 0x07: // ObjectDescriptor
377- //case 0x08: // EXTERNAL
378- //case 0x09: // REAL
379- case 0x0A: // ENUMERATED
380- return this.stream.parseInteger(content, content + len);
381- //case 0x0B: // EMBEDDED_PDV
382- case 0x10: // SEQUENCE
383- case 0x11: // SET
384- if (this.sub !== null)
385- return "(" + this.sub.length + " elem)";
386- else
387- return "(no elem)";
388- case 0x0C: // UTF8String
389- return stringCut(this.stream.parseStringUTF(content, content + len), maxLength);
390- case 0x12: // NumericString
391- case 0x13: // PrintableString
392- case 0x14: // TeletexString
393- case 0x15: // VideotexString
394- case 0x16: // IA5String
395- case 0x1A: // VisibleString
396- case 0x1B: // GeneralString
397- //case 0x19: // GraphicString
398- //case 0x1C: // UniversalString
399- return stringCut(this.stream.parseStringISO(content, content + len), maxLength);
400- case 0x1E: // BMPString
401- return stringCut(this.stream.parseStringBMP(content, content + len), maxLength);
402- case 0x17: // UTCTime
403- case 0x18: // GeneralizedTime
404- return this.stream.parseTime(content, content + len, (this.tag.tagNumber == 0x17));
0000405 }
406- return null;
407-};
408-ASN1.prototype.toString = function () {
409- return this.typeName() + "@" + this.stream.pos + "[header:" + this.header + ",length:" + this.length + ",sub:" + ((this.sub === null) ? 'null' : this.sub.length) + "]";
410-};
411-ASN1.prototype.toPrettyString = function (indent) {
412- if (indent === undefined) indent = '';
413- var s = indent + this.typeName() + " @" + this.stream.pos;
414- if (this.length >= 0)
415- s += "+";
416- s += this.length;
417- if (this.tag.tagConstructed)
418- s += " (constructed)";
419- else if ((this.tag.isUniversal() && ((this.tag.tagNumber == 0x03) || (this.tag.tagNumber == 0x04))) && (this.sub !== null))
420- s += " (encapsulates)";
421- var content = this.content();
422- if (content)
423- s += ": " + content.replace(/\n/g, '|');
424- s += "\n";
425- if (this.sub !== null) {
426- indent += ' ';
427- for (var i = 0, max = this.sub.length; i < max; ++i)
428- s += this.sub[i].toPrettyString(indent);
429 }
430- return s;
431-};
432-ASN1.prototype.posStart = function () {
433- return this.stream.pos;
434-};
435-ASN1.prototype.posContent = function () {
436- return this.stream.pos + this.header;
437-};
438-ASN1.prototype.posEnd = function () {
439- return this.stream.pos + this.header + Math.abs(this.length);
440-};
441-/** Position of the length. */
442-ASN1.prototype.posLen = function() {
443- return this.stream.pos + this.tagLen;
444-};
445-ASN1.prototype.toHexString = function () {
446- return this.stream.hexDump(this.posStart(), this.posEnd(), true);
447-};
448-ASN1.prototype.toB64String = function () {
449- return this.stream.b64Dump(this.posStart(), this.posEnd());
450-};
451-ASN1.decodeLength = function (stream) {
452- var buf = stream.get(),
453- len = buf & 0x7F;
454- if (len == buf) // first bit was 0, short form
455- return len;
456- if (len === 0) // long form with length 0 is a special case
457- return null; // undefined length
458- if (len > 6) // no reason to use Int10, as it would be a huge buffer anyways
459- throw "Length over 48 bits not supported at position " + (stream.pos - 1);
460- buf = 0;
461- for (var i = 0; i < len; ++i)
462- buf = (buf * 256) + stream.get();
463- return buf;
464-};
465-function ASN1Tag(stream) {
466- var buf = stream.get();
467- this.tagClass = buf >> 6;
468- this.tagConstructed = ((buf & 0x20) !== 0);
469- this.tagNumber = buf & 0x1F;
470- if (this.tagNumber == 0x1F) { // long tag
471- var n = new Int10();
472- do {
473- buf = stream.get();
474- n.mulAdd(128, buf & 0x7F);
475- } while (buf & 0x80);
476- this.tagNumber = n.simplify();
477 }
478-}
479-ASN1Tag.prototype.isUniversal = function () {
480- return this.tagClass === 0x00;
481-};
482-ASN1Tag.prototype.isEOC = function () {
483- return this.tagClass === 0x00 && this.tagNumber === 0x00;
484-};
485-ASN1.decode = function (stream, offset) {
486- if (!(stream instanceof Stream))
487- stream = new Stream(stream, offset || 0);
488- var streamStart = new Stream(stream),
489- tag = new ASN1Tag(stream),
490- tagLen = stream.pos - streamStart.pos,
491- len = ASN1.decodeLength(stream),
492- start = stream.pos,
493- header = start - streamStart.pos,
494- sub = null,
495- getSub = function () {
496- sub = [];
497- if (len !== null) {
498- // definite length
499- var end = start + len;
500- if (end > stream.enc.length)
501- throw 'Container at offset ' + start + ' has a length of ' + len + ', which is past the end of the stream';
502- while (stream.pos < end)
503- sub[sub.length] = ASN1.decode(stream);
504- if (stream.pos != end)
505- throw 'Content size is not correct for container at offset ' + start;
506- } else {
507- // undefined length
508- try {
509- for (;;) {
510- var s = ASN1.decode(stream);
511- if (s.tag.isEOC())
512- break;
513- sub[sub.length] = s;
0000000000000000000514 }
515- len = start - stream.pos; // undefined lengths are represented as negative values
516- } catch (e) {
517- throw 'Exception while decoding undefined length content at offset ' + start + ': ' + e;
518 }
0000519 }
520- };
521- if (tag.tagConstructed) {
522- // must have valid content
523- getSub();
524- } else if (tag.isUniversal() && ((tag.tagNumber == 0x03) || (tag.tagNumber == 0x04))) {
525- // sometimes BitString and OctetString are used to encapsulate ASN.1
526- try {
527- if (tag.tagNumber == 0x03)
528- if (stream.get() != 0)
529- throw "BIT STRINGs with unused bits cannot encapsulate.";
530- getSub();
531- for (var i = 0; i < sub.length; ++i)
532- if (sub[i].tag.isEOC())
533- throw 'EOC is not supposed to be actual content.';
534- } catch (e) {
535- // but silently ignore when they don't
536- sub = null;
537- //DEBUG console.log('Could not decode structure at ' + start + ':', e);
538 }
539- }
540- if (sub === null) {
541- if (len === null)
542- throw "We can't skip over an invalid tag with undefined length at offset " + start;
543- stream.pos = start + Math.abs(len);
544 }
545- return new ASN1(streamStart, header, len, tag, tagLen, sub);
546-};
547548-return ASN1;
549-550-});
···1// ASN.1 JavaScript decoder
2+// Copyright (c) 2008 Lapo Luchini <lapo@lapo.it>
34// Permission to use, copy, modify, and/or distribute this software for any
5// purpose with or without fee is hereby granted, provided that the above
6// copyright notice and this permission notice appear in all copies.
7+//
8// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
···13// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1516+import { Int10 } from './int10.js';
17+import { oids } from './oids.js';
0001819+const
20+ ellipsis = '\u2026',
21+ 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)?)?$/,
22+ 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)?)?$/,
23+ hexDigits = '0123456789ABCDEF',
24+ b64Std = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
25+ b64URL = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_',
26+ tableT61 = [
27+ ['', ''],
28+ ['AEIOUaeiou', 'รรรรรร รจรฌรฒรน'], // Grave
29+ ['ACEILNORSUYZacegilnorsuyz', 'รฤรรฤนลรลลรรลนรกฤรฉฤฃรญฤบลรณลลรบรฝลบ'], // Acute
30+ ['ACEGHIJOSUWYaceghijosuwy', 'รฤรฤฤครฤดรลรลดลถรขฤรชฤฤฅรฎฤตรดลรปลตลท'], // Circumflex
31+ ['AINOUainou', 'รฤจรรลจรฃฤฉรฑรตลฉ'], // Tilde
32+ ['AEIOUaeiou', 'ฤฤฤชลลชฤฤฤซลลซ'], // Macron
33+ ['AGUagu', 'ฤฤลฌฤฤลญ'], // Breve
34+ ['CEGIZcegz', 'ฤฤฤ ฤฐลปฤฤฤกลผ'], // Dot
35+ ['AEIOUYaeiouy', 'รรรรรลธรครซรฏรถรผรฟ'], // Umlaut or diรฆresis
36+ ['', ''],
37+ ['AUau', 'ร ลฎรฅลฏ'], // Ring
38+ ['CGKLNRSTcklnrst', 'รฤขฤถฤปล ลลลขรงฤทฤผลลลลฃ'], // Cedilla
39+ ['', ''],
40+ ['OUou', 'ลลฐลลฑ'], // Double Acute
41+ ['AEIUaeiu', 'ฤฤฤฎลฒฤ ฤฤฏลณ'], // Ogonek
42+ ['CDELNRSTZcdelnrstz', 'ฤฤฤฤฝลลล ลคลฝฤฤฤฤพลลลกลฅลพ'], // Caron
43+ ];
4445function stringCut(str, len) {
46 if (str.length > len)
···48 return str;
49}
5051+function checkPrintable(s) {
52+ let i, v;
53+ for (i = 0; i < s.length; ++i) {
54+ v = s.charCodeAt(i);
55+ if (v < 32 && v != 9 && v != 10 && v != 13) // [\t\r\n] are (kinda) printable
56+ throw new Error('Unprintable character at index ' + i + ' (code ' + s.str.charCodeAt(i) + ')');
0057 }
58}
59+60+/** Class to manage a stream of bytes, with a zero-copy approach.
61+ * It uses an existing array or binary string and advances a position index. */
62+export class Stream {
63+64+ /**
65+ * @param {Stream|array|string} enc data (will not be copied)
66+ * @param {?number} pos starting position (mandatory when `end` is not a Stream)
67+ */
68+ constructor(enc, pos) {
69+ if (enc instanceof Stream) {
70+ this.enc = enc.enc;
71+ this.pos = enc.pos;
72+ } else {
73+ this.enc = enc;
74+ this.pos = pos;
75+ }
76+ if (typeof this.pos != 'number')
77+ throw new Error('"pos" must be a numeric value');
78+ if (typeof this.enc == 'string')
79+ this.getRaw = pos => this.enc.charCodeAt(pos);
80+ else if (typeof this.enc[0] == 'number')
81+ this.getRaw = pos => this.enc[pos];
82+ else
83+ throw new Error('"enc" must be a numeric array or a string');
84 }
85+ /** Get the byte at current position (and increment it) or at a specified position (and avoid moving current position).
86+ * @param {?number} pos read position if specified, else current position (and increment it) */
87+ get(pos) {
88+ if (pos === undefined)
89+ pos = this.pos++;
90+ if (pos >= this.enc.length)
91+ throw new Error('Requesting byte offset ' + pos + ' on a stream of length ' + this.enc.length);
92+ return this.getRaw(pos);
0000093 }
94+ /** Convert a single byte to an hexadcimal string (of length 2).
95+ * @param {number} b */
96+ static hexByte(b) {
97+ return hexDigits.charAt((b >> 4) & 0xF) + hexDigits.charAt(b & 0xF);
0098 }
99+ /** Hexadecimal dump of a specified region of the stream.
100+ * @param {number} start starting position (included)
101+ * @param {number} end ending position (excluded)
102+ * @param {string} type 'raw', 'byte' or 'dump' (default) */
103+ hexDump(start, end, type = 'dump') {
104+ let s = '';
105+ for (let i = start; i < end; ++i) {
106+ if (type == 'byte' && i > start)
107+ s += ' ';
108+ s += Stream.hexByte(this.get(i));
109+ if (type == 'dump')
110+ switch (i & 0xF) {
111+ case 0x7: s += ' '; break;
112+ case 0xF: s += '\n'; break;
113+ default: s += ' ';
114+ }
115+ }
116+ return s;
117 }
118+ /** Base64url dump of a specified region of the stream (according to RFC 4648 section 5).
119+ * @param {number} start starting position (included)
120+ * @param {number} end ending position (excluded)
121+ * @param {string} type 'url' (default, section 5 without padding) or 'std' (section 4 with padding) */
122+ b64Dump(start, end, type = 'url') {
123+ const b64 = type === 'url' ? b64URL : b64Std;
124+ let extra = (end - start) % 3,
125+ s = '',
126+ i, c;
127+ for (i = start; i + 2 < end; i += 3) {
128+ c = this.get(i) << 16 | this.get(i + 1) << 8 | this.get(i + 2);
129+ s += b64.charAt(c >> 18 & 0x3F);
130+ s += b64.charAt(c >> 12 & 0x3F);
131+ s += b64.charAt(c >> 6 & 0x3F);
132+ s += b64.charAt(c & 0x3F);
133+ }
134+ if (extra > 0) {
135+ c = this.get(i) << 16;
136+ if (extra > 1) c |= this.get(i + 1) << 8;
137+ s += b64.charAt(c >> 18 & 0x3F);
138+ s += b64.charAt(c >> 12 & 0x3F);
139+ if (extra == 2) s += b64.charAt(c >> 6 & 0x3F);
140+ if (b64 === b64Std) s += '==='.slice(0, 3 - extra);
141+ }
142+ return s;
143 }
144+ isASCII(start, end) {
145+ for (let i = start; i < end; ++i) {
146+ let c = this.get(i);
147+ if (c < 32 || c > 176)
148+ return false;
149+ }
150+ return true;
151 }
152+ parseStringISO(start, end, maxLength) {
153+ let s = '';
154+ for (let i = start; i < end; ++i)
155+ s += String.fromCharCode(this.get(i));
156+ return { size: s.length, str: stringCut(s, maxLength) };
0000000000157 }
158+ parseStringT61(start, end, maxLength) {
159+ // warning: this code is not very well tested so far
160+ function merge(c, d) {
161+ let t = tableT61[c - 0xC0];
162+ let i = t[0].indexOf(String.fromCharCode(d));
163+ return (i < 0) ? '\0' : t[1].charAt(i);
164+ }
165+ let s = '', c;
166+ for (let i = start; i < end; ++i) {
167+ c = this.get(i);
168+ if (c >= 0xA4 && c <= 0xBF)
169+ s += '$ยฅ#ยงยค\0\0ยซ\0\0\0\0ยฐยฑยฒยณรยตยถยทรท\0\0ยปยผยฝยพยฟ'.charAt(c - 0xA4);
170+ else if (c >= 0xE0 && c <= 0xFF)
171+ s += 'โฆรรยชฤฆ\0ฤฒฤฟลรลยบรลฆลลฤธรฆฤรฐฤงฤฑฤณลลรธลรรพลงล\0'.charAt(c - 0xE0);
172+ else if (c >= 0xC0 && c <= 0xCF)
173+ s += merge(c, this.get(++i));
174+ else // using ISO 8859-1 for characters undefined (or equal) in T.61
175+ s += String.fromCharCode(c);
176+ }
177+ return { size: s.length, str: stringCut(s, maxLength) };
178 }
179+ parseStringUTF(start, end, maxLength) {
180+ function ex(c) { // must be 10xxxxxx
181+ if ((c < 0x80) || (c >= 0xC0))
182+ throw new Error('Invalid UTF-8 continuation byte: ' + c);
183+ return (c & 0x3F);
184+ }
185+ function surrogate(cp) {
186+ if (cp < 0x10000)
187+ throw new Error('UTF-8 overlong encoding, codepoint encoded in 4 bytes: ' + cp);
188+ // we could use String.fromCodePoint(cp) but let's be nice to older browsers and use surrogate pairs
189+ cp -= 0x10000;
190+ return String.fromCharCode((cp >> 10) + 0xD800, (cp & 0x3FF) + 0xDC00);
191+ }
192+ let s = '';
193+ for (let i = start; i < end; ) {
194+ let c = this.get(i++);
195+ if (c < 0x80) // 0xxxxxxx (7 bit)
196+ s += String.fromCharCode(c);
197+ else if (c < 0xC0)
198+ throw new Error('Invalid UTF-8 starting byte: ' + c);
199+ else if (c < 0xE0) // 110xxxxx 10xxxxxx (11 bit)
200+ s += String.fromCharCode(((c & 0x1F) << 6) | ex(this.get(i++)));
201+ else if (c < 0xF0) // 1110xxxx 10xxxxxx 10xxxxxx (16 bit)
202+ s += String.fromCharCode(((c & 0x0F) << 12) | (ex(this.get(i++)) << 6) | ex(this.get(i++)));
203+ else if (c < 0xF8) // 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx (21 bit)
204+ s += surrogate(((c & 0x07) << 18) | (ex(this.get(i++)) << 12) | (ex(this.get(i++)) << 6) | ex(this.get(i++)));
205+ else
206+ throw new Error('Invalid UTF-8 starting byte (since 2003 it is restricted to 4 bytes): ' + c);
207+ }
208+ return { size: s.length, str: stringCut(s, maxLength) };
209 }
210+ parseStringBMP(start, end, maxLength) {
211+ let s = '', hi, lo;
212+ for (let i = start; i < end; ) {
213+ hi = this.get(i++);
214+ lo = this.get(i++);
215+ s += String.fromCharCode((hi << 8) | lo);
0216 }
217+ return { size: s.length, str: stringCut(s, maxLength) };
218 }
219+ parseTime(start, end, shortYear) {
220+ let s = this.parseStringISO(start, end).str,
221+ m = (shortYear ? reTimeS : reTimeL).exec(s);
222+ if (!m)
223+ throw new Error('Unrecognized time: ' + s);
224+ if (shortYear) {
225+ // to avoid querying the timer, use the fixed range [1970, 2069]
226+ // it will conform with ITU X.400 [-10, +40] sliding window until 2030
227+ m[1] = +m[1];
228+ m[1] += (m[1] < 70) ? 2000 : 1900;
229+ }
230+ s = m[1] + '-' + m[2] + '-' + m[3] + ' ' + m[4];
231+ if (m[5]) {
232+ s += ':' + m[5];
233+ if (m[6]) {
234+ s += ':' + m[6];
235+ if (m[7])
236+ s += '.' + m[7];
237+ }
238+ }
239+ if (m[8]) {
240+ s += ' UTC';
241 if (m[9])
242+ s += m[9] + ':' + (m[10] || '00');
243 }
244+ return s;
245 }
246+ parseInteger(start, end) {
247+ let v = this.get(start),
248+ neg = (v > 127),
249+ pad = neg ? 255 : 0,
250+ len,
251+ s = '';
252+ // skip unuseful bits (not allowed in DER)
253+ while (v == pad && ++start < end)
254+ v = this.get(start);
255+ len = end - start;
256+ if (len === 0)
257+ return neg ? '-1' : '0';
258+ // show bit length of huge integers
259+ if (len > 4) {
260+ s = v;
261+ len <<= 3;
262+ while (((s ^ pad) & 0x80) == 0) {
263+ s <<= 1;
264+ --len;
265+ }
266+ s = '(' + len + ' bit)\n';
267 }
268+ // decode the integer
269+ if (neg) v = v - 256;
270+ let n = new Int10(v);
271+ for (let i = start + 1; i < end; ++i)
272+ n.mulAdd(256, this.get(i));
273+ return s + n.toString();
274 }
275+ parseBitString(start, end, maxLength) {
276+ let unusedBits = this.get(start);
277+ if (unusedBits > 7)
278+ throw new Error('Invalid BitString with unusedBits=' + unusedBits);
279+ let lenBit = ((end - start - 1) << 3) - unusedBits,
280+ s = '';
281+ for (let i = start + 1; i < end; ++i) {
282+ let b = this.get(i),
283+ skip = (i == end - 1) ? unusedBits : 0;
284+ for (let j = 7; j >= skip; --j)
285+ s += (b >> j) & 1 ? '1' : '0';
286+ if (s.length > maxLength)
287+ s = stringCut(s, maxLength);
288+ }
289+ return { size: lenBit, str: s };
00000290 }
291+ parseOctetString(start, end, maxLength) {
292+ let len = end - start,
293+ s;
294+ try {
295+ s = this.parseStringUTF(start, end, maxLength);
296+ checkPrintable(s.str);
297+ return { size: end - start, str: s.str };
298+ } catch (e) {
299+ // ignore
000300 }
301+ maxLength /= 2; // we work in bytes
302+ if (len > maxLength)
303+ end = start + maxLength;
304+ s = '';
305+ for (let i = start; i < end; ++i)
306+ s += Stream.hexByte(this.get(i));
307+ if (len > maxLength)
308+ s += ellipsis;
309 return { size: len, str: s };
00310 }
311+ parseOID(start, end, maxLength, isRelative) {
312+ let s = '',
313+ n = new Int10(),
000000000000000000000000000000314 bits = 0;
315+ for (let i = start; i < end; ++i) {
316+ let v = this.get(i);
317+ n.mulAdd(128, v & 0x7F);
318+ bits += 7;
319+ if (!(v & 0x80)) { // finished
320+ if (s === '') {
321+ n = n.simplify();
322+ if (isRelative) {
323+ s = (n instanceof Int10) ? n.toString() : '' + n;
324+ } else if (n instanceof Int10) {
325+ n.sub(80);
326+ s = '2.' + n.toString();
327+ } else {
328+ let m = n < 80 ? n < 40 ? 0 : 1 : 2;
329+ s = m + '.' + (n - m * 40);
330+ }
331+ } else
332+ s += '.' + n.toString();
333+ if (s.length > maxLength)
334+ return stringCut(s, maxLength);
335+ n = new Int10();
336+ bits = 0;
337+ }
338 }
339+ if (bits > 0)
340+ s += '.incomplete';
341+ if (typeof oids === 'object' && !isRelative) {
342+ let oid = oids[s];
343+ if (oid) {
344+ if (oid.d) s += '\n' + oid.d;
345+ if (oid.c) s += '\n' + oid.c;
346+ if (oid.w) s += '\n(warning!)';
347+ }
348+ }
349+ return s;
350 }
351+ parseRelativeOID(start, end, maxLength) {
352+ return this.parseOID(start, end, maxLength, true);
0000000353 }
00000000000354}
355+0000000000000000000000000000000000000356function recurse(el, parser, maxLength) {
357+ let avoidRecurse = true;
358 if (el.tag.tagConstructed && el.sub) {
359 avoidRecurse = false;
360 el.sub.forEach(function (e1) {
···364 }
365 if (avoidRecurse)
366 return el.stream[parser](el.posContent(), el.posContent() + Math.abs(el.length), maxLength);
367+ let d = { size: 0, str: '' };
368 el.sub.forEach(function (el) {
369+ let d1 = recurse(el, parser, maxLength - d.str.length);
370 d.size += d1.size;
371 d.str += d1.str;
372 });
373 return d;
374}
375+376+class ASN1Tag {
377+ constructor(stream) {
378+ let buf = stream.get();
379+ this.tagClass = buf >> 6;
380+ this.tagConstructed = ((buf & 0x20) !== 0);
381+ this.tagNumber = buf & 0x1F;
382+ if (this.tagNumber == 0x1F) { // long tag
383+ let n = new Int10();
384+ do {
385+ buf = stream.get();
386+ n.mulAdd(128, buf & 0x7F);
387+ } while (buf & 0x80);
388+ this.tagNumber = n.simplify();
389+ }
390+ }
391+ isUniversal() {
392+ return this.tagClass === 0x00;
393+ }
394+ isEOC() {
395+ return this.tagClass === 0x00 && this.tagNumber === 0x00;
396+ }
397+}
398+399+export class ASN1 {
400+ constructor(stream, header, length, tag, tagLen, sub) {
401+ if (!(tag instanceof ASN1Tag)) throw new Error('Invalid tag value.');
402+ this.stream = stream;
403+ this.header = header;
404+ this.length = length;
405+ this.tag = tag;
406+ this.tagLen = tagLen;
407+ this.sub = sub;
408+ }
409+ typeName() {
410+ switch (this.tag.tagClass) {
411+ case 0: // universal
412+ switch (this.tag.tagNumber) {
413+ case 0x00: return 'EOC';
414+ case 0x01: return 'BOOLEAN';
415+ case 0x02: return 'INTEGER';
416+ case 0x03: return 'BIT_STRING';
417+ case 0x04: return 'OCTET_STRING';
418+ case 0x05: return 'NULL';
419+ case 0x06: return 'OBJECT_IDENTIFIER';
420+ case 0x07: return 'ObjectDescriptor';
421+ case 0x08: return 'EXTERNAL';
422+ case 0x09: return 'REAL';
423+ case 0x0A: return 'ENUMERATED';
424+ case 0x0B: return 'EMBEDDED_PDV';
425+ case 0x0C: return 'UTF8String';
426+ case 0x0D: return 'RELATIVE_OID';
427+ case 0x10: return 'SEQUENCE';
428+ case 0x11: return 'SET';
429+ case 0x12: return 'NumericString';
430+ case 0x13: return 'PrintableString'; // ASCII subset
431+ case 0x14: return 'TeletexString'; // aka T61String
432+ case 0x15: return 'VideotexString';
433+ case 0x16: return 'IA5String'; // ASCII
434+ case 0x17: return 'UTCTime';
435+ case 0x18: return 'GeneralizedTime';
436+ case 0x19: return 'GraphicString';
437+ case 0x1A: return 'VisibleString'; // ASCII subset
438+ case 0x1B: return 'GeneralString';
439+ case 0x1C: return 'UniversalString';
440+ case 0x1E: return 'BMPString';
441+ }
442+ return 'Universal_' + this.tag.tagNumber.toString();
443+ case 1: return 'Application_' + this.tag.tagNumber.toString();
444+ case 2: return '[' + this.tag.tagNumber.toString() + ']'; // Context
445+ case 3: return 'Private_' + this.tag.tagNumber.toString();
446+ }
447+ }
448+ /** A string preview of the content (intended for humans). */
449+ content(maxLength) {
450+ if (this.tag === undefined)
451+ return null;
452+ if (maxLength === undefined)
453+ maxLength = Infinity;
454+ let content = this.posContent(),
455+ len = Math.abs(this.length);
456+ if (!this.tag.isUniversal()) {
457+ if (this.sub !== null)
458+ return '(' + this.sub.length + ' elem)';
459+ let d1 = this.stream.parseOctetString(content, content + len, maxLength);
460+ return '(' + d1.size + ' byte)\n' + d1.str;
461+ }
462+ switch (this.tag.tagNumber) {
463+ case 0x01: // BOOLEAN
464+ return (this.stream.get(content) === 0) ? 'false' : 'true';
465+ case 0x02: // INTEGER
466+ return this.stream.parseInteger(content, content + len);
467+ case 0x03: { // BIT_STRING
468+ let d = recurse(this, 'parseBitString', maxLength);
469+ return '(' + d.size + ' bit)\n' + d.str;
470+ }
471+ case 0x04: { // OCTET_STRING
472+ let d = recurse(this, 'parseOctetString', maxLength);
473+ return '(' + d.size + ' byte)\n' + d.str;
474+ }
475+ //case 0x05: // NULL
476+ case 0x06: // OBJECT_IDENTIFIER
477+ return this.stream.parseOID(content, content + len, maxLength);
478+ //case 0x07: // ObjectDescriptor
479+ //case 0x08: // EXTERNAL
480+ //case 0x09: // REAL
481+ case 0x0A: // ENUMERATED
482+ return this.stream.parseInteger(content, content + len);
483+ //case 0x0B: // EMBEDDED_PDV
484+ case 0x0D: // RELATIVE-OID
485+ return this.stream.parseRelativeOID(content, content + len, maxLength);
486+ case 0x10: // SEQUENCE
487+ case 0x11: // SET
488+ if (this.sub !== null)
489+ return '(' + this.sub.length + ' elem)';
490+ else
491+ return '(no elem)';
492+ case 0x0C: // UTF8String
493+ return recurse(this, 'parseStringUTF', maxLength).str;
494+ case 0x14: // TeletexString
495+ return recurse(this, 'parseStringT61', maxLength).str;
496+ case 0x12: // NumericString
497+ case 0x13: // PrintableString
498+ case 0x15: // VideotexString
499+ case 0x16: // IA5String
500+ case 0x1A: // VisibleString
501+ case 0x1B: // GeneralString
502+ //case 0x19: // GraphicString
503+ //case 0x1C: // UniversalString
504+ return recurse(this, 'parseStringISO', maxLength).str;
505+ case 0x1E: // BMPString
506+ return recurse(this, 'parseStringBMP', maxLength).str;
507+ case 0x17: // UTCTime
508+ case 0x18: // GeneralizedTime
509+ return this.stream.parseTime(content, content + len, (this.tag.tagNumber == 0x17));
510+ }
511 return null;
512+ }
513+ toString() {
514+ return this.typeName() + '@' + this.stream.pos + '[header:' + this.header + ',length:' + this.length + ',sub:' + ((this.sub === null) ? 'null' : this.sub.length) + ']';
000000515 }
516+ toPrettyString(indent) {
517+ if (indent === undefined) indent = '';
518+ let s = indent;
519+ if (this.def) {
520+ if (this.def.id)
521+ s += this.def.id + ' ';
522+ if (this.def.name && this.def.name != this.typeName().replace(/_/g, ' '))
523+ s+= this.def.name + ' ';
524+ if (this.def.mismatch)
525+ s += '[?] ';
526+ }
527+ s += this.typeName() + ' @' + this.stream.pos;
528+ if (this.length >= 0)
529+ s += '+';
530+ s += this.length;
531+ if (this.tag.tagConstructed)
532+ s += ' (constructed)';
533+ else if ((this.tag.isUniversal() && ((this.tag.tagNumber == 0x03) || (this.tag.tagNumber == 0x04))) && (this.sub !== null))
534+ s += ' (encapsulates)';
535+ let content = this.content();
536+ if (content)
537+ s += ': ' + content.replace(/\n/g, '|');
538+ s += '\n';
539+ if (this.sub !== null) {
540+ indent += ' ';
541+ for (let i = 0, max = this.sub.length; i < max; ++i)
542+ s += this.sub[i].toPrettyString(indent);
543+ }
544+ return s;
545+ }
546+ posStart() {
547+ return this.stream.pos;
548+ }
549+ posContent() {
550+ return this.stream.pos + this.header;
551+ }
552+ posEnd() {
553+ return this.stream.pos + this.header + Math.abs(this.length);
554+ }
555+ /** Position of the length. */
556+ posLen() {
557+ return this.stream.pos + this.tagLen;
558+ }
559+ /** Hexadecimal dump of the node.
560+ * @param type 'raw', 'byte' or 'dump' */
561+ toHexString(type = 'raw') {
562+ return this.stream.hexDump(this.posStart(), this.posEnd(), type);
563 }
564+ /** Base64url dump of the node (according to RFC 4648 section 5).
565+ * @param {string} type 'url' (default, section 5 without padding) or 'std' (section 4 with padding)
566+ */
567+ toB64String(type = 'url') {
568+ return this.stream.b64Dump(this.posStart(), this.posEnd(), type);
000000000000000000569 }
570+ static decodeLength(stream) {
571+ let buf = stream.get(),
572+ len = buf & 0x7F;
573+ if (len == buf) // first bit was 0, short form
574+ return len;
575+ if (len === 0) // long form with length 0 is a special case
576+ return null; // undefined length
577+ if (len > 6) // no reason to use Int10, as it would be a huge buffer anyways
578+ throw new Error('Length over 48 bits not supported at position ' + (stream.pos - 1));
579+ buf = 0;
580+ for (let i = 0; i < len; ++i)
581+ buf = (buf * 256) + stream.get();
582+ return buf;
0000000000000000000000000000000000583 }
584+ static decode(stream, offset, type = ASN1) {
585+ if (!(type == ASN1 || type.prototype instanceof ASN1))
586+ throw new Error('Must pass a class that extends ASN1');
587+ if (!(stream instanceof Stream))
588+ stream = new Stream(stream, offset || 0);
589+ let streamStart = new Stream(stream),
590+ tag = new ASN1Tag(stream),
591+ tagLen = stream.pos - streamStart.pos,
592+ len = ASN1.decodeLength(stream),
593+ start = stream.pos,
594+ header = start - streamStart.pos,
595+ sub = null,
596+ getSub = function () {
597+ sub = [];
598+ if (len !== null) {
599+ // definite length
600+ let end = start + len;
601+ if (end > stream.enc.length)
602+ throw new Error('Container at offset ' + start + ' has a length of ' + len + ', which is past the end of the stream');
603+ while (stream.pos < end)
604+ sub[sub.length] = type.decode(stream);
605+ if (stream.pos != end)
606+ throw new Error('Content size is not correct for container at offset ' + start);
607+ } else {
608+ // undefined length
609+ try {
610+ for (;;) {
611+ let s = type.decode(stream);
612+ if (s.tag.isEOC())
613+ break;
614+ sub[sub.length] = s;
615+ }
616+ len = start - stream.pos; // undefined lengths are represented as negative values
617+ } catch (e) {
618+ throw new Error('Exception while decoding undefined length content at offset ' + start + ': ' + e);
619+ }
620+ }
621+ };
622+ if (tag.tagConstructed) {
623+ // must have valid content
624+ getSub();
625+ } else if (tag.isUniversal() && ((tag.tagNumber == 0x03) || (tag.tagNumber == 0x04))) {
626+ // sometimes BitString and OctetString are used to encapsulate ASN.1
627+ try {
628+ if (tag.tagNumber == 0x03)
629+ if (stream.get() != 0)
630+ throw new Error('BIT STRINGs with unused bits cannot encapsulate.');
631+ getSub();
632+ for (let s of sub) {
633+ if (s.tag.isEOC())
634+ throw new Error('EOC is not supposed to be actual content.');
635+ try {
636+ s.content();
637+ } catch (e) {
638+ throw new Error('Unable to parse content: ' + e);
639 }
000640 }
641+ } catch (e) {
642+ // but silently ignore when they don't
643+ sub = null;
644+ //DEBUG console.log('Could not decode structure at ' + start + ':', e);
645 }
646+ }
647+ if (sub === null) {
648+ if (len === null)
649+ throw new Error("We can't skip over an invalid tag with undefined length at offset " + start);
650+ stream.pos = start + Math.abs(len);
0000000000000651 }
652+ return new type(streamStart, header, len, tag, tagLen, sub);
0000653 }
00654655+}
00
+79-82
base64.js
···1// Base64 JavaScript decoder
2-// Copyright (c) 2008-2021 Lapo Luchini <lapo@lapo.it>
34// Permission to use, copy, modify, and/or distribute this software for any
5// purpose with or without fee is hereby granted, provided that the above
6// copyright notice and this permission notice appear in all copies.
7-//
8// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
···13// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1516-(typeof define != 'undefined' ? define : function (factory) { 'use strict';
17- if (typeof module == 'object') module.exports = factory();
18- else window.base64 = factory();
19-})(function () {
20-"use strict";
2122-var Base64 = {},
23- decoder, // populated on first usage
24- haveU8 = (typeof Uint8Array == 'function');
2526-Base64.decode = function (a) {
27- var isString = (typeof a == 'string');
28- var i;
29- if (decoder === undefined) {
30- var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",
31- ignore = "= \f\n\r\t\u00A0\u2028\u2029";
32- decoder = [];
33- for (i = 0; i < 64; ++i)
34- decoder[b64.charCodeAt(i)] = i;
35- for (i = 0; i < ignore.length; ++i)
36- decoder[ignore.charCodeAt(i)] = -1;
37- // RFC 3548 URL & file safe encoding
38- decoder['-'.charCodeAt(0)] = decoder['+'.charCodeAt(0)];
39- decoder['_'.charCodeAt(0)] = decoder['/'.charCodeAt(0)];
40- }
41- var out = haveU8 ? new Uint8Array(a.length * 3 >> 2) : [];
42- var bits = 0, char_count = 0, len = 0;
43- for (i = 0; i < a.length; ++i) {
44- var c = isString ? a.charCodeAt(i) : a[i];
45- if (c == 61) // '='.charCodeAt(0)
000000000000000000000046 break;
47- c = decoder[c];
48- if (c == -1)
49- continue;
50- if (c === undefined)
51- throw 'Illegal character at offset ' + i;
52- bits |= c;
53- if (++char_count >= 4) {
54 out[len++] = (bits >> 16);
55 out[len++] = (bits >> 8) & 0xFF;
56- out[len++] = bits & 0xFF;
57- bits = 0;
58- char_count = 0;
59- } else {
60- bits <<= 6;
61 }
62- }
63- switch (char_count) {
64- case 1:
65- throw "Base64 encoding incomplete: at least 2 bits missing";
66- case 2:
67- out[len++] = (bits >> 10);
68- break;
69- case 3:
70- out[len++] = (bits >> 16);
71- out[len++] = (bits >> 8) & 0xFF;
72- break;
73 }
74- if (haveU8 && out.length > len) // in case it was originally longer because of ignored characters
75- out = out.subarray(0, len);
76- return out;
77-};
7879-Base64.pretty = function (str) {
80- // fix padding
81- if (str.length % 4 > 0)
82- str = (str + '===').slice(0, str.length + str.length % 4);
83- // convert RFC 3548 to standard Base64
84- str = str.replace(/-/g, '+').replace(/_/g, '/');
85- // 80 column width
86- return str.replace(/(.{80})/g, '$1\n');
87-};
08889-Base64.re = /-----BEGIN [^-]+-----([A-Za-z0-9+/=\s]+)-----END [^-]+-----|begin-base64[^\n]+\n([A-Za-z0-9+/=\s]+)====|^([A-Za-z0-9+/=\s]+)$/;
90-Base64.unarmor = function (a) {
91- var m = Base64.re.exec(a);
92- if (m) {
93- if (m[1])
94- a = m[1];
95- else if (m[2])
96- a = m[2];
97- else if (m[3])
98- a = m[3];
99- else
100- throw "RegExp out of sync";
0101 }
102- return Base64.decode(a);
103-};
104105-return Base64;
106107-});
···1// Base64 JavaScript decoder
2+// Copyright (c) 2008 Lapo Luchini <lapo@lapo.it>
34// Permission to use, copy, modify, and/or distribute this software for any
5// purpose with or without fee is hereby granted, provided that the above
6// copyright notice and this permission notice appear in all copies.
7+//
8// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
···13// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1516+const
17+ haveU8 = (typeof Uint8Array == 'function');
18+19+let decoder; // populated on first usage
02021+export class Base64 {
002223+ static decode(a) {
24+ let isString = (typeof a == 'string');
25+ let i;
26+ if (decoder === undefined) {
27+ let b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
28+ ignore = '= \f\n\r\t\u00A0\u2028\u2029';
29+ decoder = [];
30+ for (i = 0; i < 64; ++i)
31+ decoder[b64.charCodeAt(i)] = i;
32+ for (i = 0; i < ignore.length; ++i)
33+ decoder[ignore.charCodeAt(i)] = -1;
34+ // also support decoding Base64url (RFC 4648 section 5)
35+ decoder['-'.charCodeAt(0)] = decoder['+'.charCodeAt(0)];
36+ decoder['_'.charCodeAt(0)] = decoder['/'.charCodeAt(0)];
37+ }
38+ let out = haveU8 ? new Uint8Array(a.length * 3 >> 2) : [];
39+ let bits = 0, char_count = 0, len = 0;
40+ for (i = 0; i < a.length; ++i) {
41+ let c = isString ? a.charCodeAt(i) : a[i];
42+ if (c == 61) // '='.charCodeAt(0)
43+ break;
44+ c = decoder[c];
45+ if (c == -1)
46+ continue;
47+ if (c === undefined)
48+ throw 'Illegal character at offset ' + i;
49+ bits |= c;
50+ if (++char_count >= 4) {
51+ out[len++] = (bits >> 16);
52+ out[len++] = (bits >> 8) & 0xFF;
53+ out[len++] = bits & 0xFF;
54+ bits = 0;
55+ char_count = 0;
56+ } else {
57+ bits <<= 6;
58+ }
59+ }
60+ switch (char_count) {
61+ case 1:
62+ throw 'Base64 encoding incomplete: at least 2 bits missing';
63+ case 2:
64+ out[len++] = (bits >> 10);
65 break;
66+ case 3:
00000067 out[len++] = (bits >> 16);
68 out[len++] = (bits >> 8) & 0xFF;
69+ break;
000070 }
71+ if (haveU8 && out.length > len) // in case it was originally longer because of ignored characters
72+ out = out.subarray(0, len);
73+ return out;
0000000074 }
00007576+ static pretty(str) {
77+ // fix padding
78+ let pad = 4 - str.length % 4;
79+ if (pad < 4)
80+ str += '==='.slice(0, pad);
81+ // convert Base64url (RFC 4648 section 5) to standard Base64 (RFC 4648 section 4)
82+ str = str.replace(/-/g, '+').replace(/_/g, '/');
83+ // 80 column width
84+ return str.replace(/.{80}/g, '$&\n');
85+ }
8687+ static unarmor(a) {
88+ let m = Base64.re.exec(a);
89+ if (m) {
90+ if (m[1])
91+ a = m[1];
92+ else if (m[2])
93+ a = m[2];
94+ else if (m[3])
95+ a = m[3];
96+ else
97+ throw 'RegExp out of sync';
98+ }
99+ return Base64.decode(a);
100 }
00101102+}
103104+Base64.re = /-----BEGIN [^-]+-----([A-Za-z0-9+/=\s]+)-----END [^-]+-----|begin-base64[^\n]+\n([A-Za-z0-9+/=\s]+)====|^([A-Za-z0-9+/=\s]+)$/;
···1+#!/usr/bin/env node
2+3+// usage:
4+// ./dumpASN1.js filename
5+// ./dumpASN1.js data:base64,MDMCAQFjLgQACgEACgEAAgEAAgEAAQEAoA+jDQQFTnRWZXIEBAEAAAAwCgQITmV0bG9nb24===
6+7+import * as fs from 'node:fs';
8+import { Base64 } from './base64.js';
9+import { ASN1 } from './asn1.js';
10+import { Defs } from './defs.js';
11+12+const
13+ colYellow = '\x1b[33m',
14+ colBlue = '\x1b[34m',
15+ colReset = '\x1b[0m',
16+ reDataURI = /^data:(?:[a-z-]+[/][a-z.+-]+;)?base64,([A-Za-z0-9+/=\s]+)$/;
17+18+function print(value, indent) {
19+ if (indent === undefined) indent = '';
20+ const def = value.def;
21+ let name = '';
22+ if (def?.type) {
23+ if (def.id) name += colBlue + def.id + colReset;
24+ if (typeof def.type == 'object' && def.name) name = (name ? name + ' ' : '') + def.name;
25+ if (def.mismatch) name = (name ? name + ' ' : '') + '[?]';
26+ if (name) name += ' ';
27+ }
28+ let s = indent + name + colYellow + value.typeName() + colReset + ' @' + value.stream.pos;
29+ if (value.length >= 0)
30+ s += '+';
31+ s += value.length;
32+ if (value.tag.tagConstructed)
33+ s += ' (constructed)';
34+ else if ((value.tag.isUniversal() && ((value.tag.tagNumber == 0x03) || (value.tag.tagNumber == 0x04))) && (value.sub !== null))
35+ s += ' (encapsulates)';
36+ let content = value.content();
37+ if (content)
38+ s += ': ' + content.replace(/\n/g, '|');
39+ s += '\n';
40+ if (value.sub !== null) {
41+ indent += ' ';
42+ for (const subval of value.sub)
43+ s += print(subval, indent);
44+ }
45+ return s;
46+}
47+48+const filename = process.argv[2];
49+const match = reDataURI.exec(filename);
50+let content = match
51+ ? Buffer.from(match[1])
52+ : fs.readFileSync(filename);
53+try { // try PEM first
54+ content = Base64.unarmor(content);
55+} catch (e) { // try DER/BER then
56+}
57+let result = ASN1.decode(content);
58+content = null;
59+const t0 = performance.now();
60+if (process.argv.length == 5) {
61+ Defs.match(result, Defs.moduleAndType(Defs.RFC[process.argv[3]], process.argv[4]));
62+} else {
63+ const types = Defs.commonTypes
64+ .map(type => {
65+ const stats = Defs.match(result, type);
66+ return { type, match: stats.recognized / stats.total };
67+ })
68+ .sort((a, b) => b.match - a.match);
69+ const t1 = performance.now();
70+ console.log('Parsed in ' + (t1 - t0).toFixed(2) + ' ms; possible types:');
71+ for (const t of types)
72+ console.log((t.match * 100).toFixed(2).padStart(6) + '% ' + t.type.description);
73+ Defs.match(result, types[0].type);
74+ // const stats = Defs.match(result, types[0].type);
75+ // console.log('Stats:', stats);
76+ console.log('Parsed as:', result.def);
77+ // const type = searchType(process.argv[2]);
78+ // const stats = applyDef(result, type);
79+}
80+console.log(print(result));
81+// console.log('Stats:', (stats.recognized * 100 / stats.total).toFixed(2) + '%');
82+// // print(result, searchType(process.argv[2]), stats);
83+// // console.log('Defs:', stats.defs);
+21
examples/cmpv2.b64
···000000000000000000000
···1+CMPv2 example as found on Wireshark page.
2+3+Original link:
4+https://wiki.wireshark.org/CMP
5+6+Attachment found moved here:
7+https://wiki.wireshark.org/uploads/__moin_import__/attachments/SampleCaptures/cmp_IR_sequence_OpenSSL-Cryptlib.pcap
8+9+begin-base64 644 cmpv2.der
10+MIICPjCB1QIBAqQCMACkRjBEMQswCQYDVQQGEwJERTEMMAoGA1UEChMDTlNOMREwDwYDVQQLEwhQ
11+RyBSREUgMzEUMBIGA1UEAxMLTWFydGluJ3MgQ0GgERgPMjAxMDA3MDUwNzM1MzhaoTwwOgYJKoZI
12+hvZ9B0INMC0EEJ5EpSD3zKjvmzHEK5+aoAAwCQYFKw4DAhoFAAICAfQwCgYIKwYBBQUIAQKiCwQJ
13+b/KGO0ILNJqApBIEEJGOKFG/9crkwU+z/I5ICa6lEgQQnnbd7EB2QjRCwOHt9QWdBKCCAUkwggFF
14+MIIBQTCBqAIBADCBoqaBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAqVTOtjEEYELkomc3sMOy
15+Too5a9YeC91IMn52cVx7doY4AeO6J9e8p+CtWNbzVF8aRgHUhh31m+/X3MkQOaY5i8nF33uxAxDL
16+MDXttHjsqrF/tsgYuuHSs/Znz4PA1kLkdhKE9DLiGlCFaJH5QY5Hzl6bcS3ApuWCny0RRzIA1/cC
17+AwEAAaGBkzANBgkqhkiG9w0BAQUFAAOBgQArOldjg75fDx7BaFp0oAknLDREvB1KyE+BV96R+lB+
18+tRRhwv3dyc/GTvRw4GtaeDjWCjNPaDCl9ZvvVljaR2aMZvhaQV+DUmCMjFSP3DPiGuszBA6R2azX
19+NKtnpJ3SGx2vk0+Iv05tXLhdnqQJZs5a3S3R30kn4Vw+4WQm3kb0fKAXAxUA9K8u+7hv5Rg6GDn6
20+aoPxbUo6fpU=
21+====
+9
examples/cms-password.p7m
···000000000
···1+This is a PKCS#7/CMS encrypted with passwod.
2+$ echo content | openssl cms -encrypt -pwri_password test -aes256 -outform pem -out examples/cms-password.p7m
3+-----BEGIN CMS-----
4+MIHYBgkqhkiG9w0BBwOggcowgccCAQMxgYOjgYACAQCgGwYJKoZIhvcNAQUMMA4E
5+CED/DSxXMtH6AgIIADAsBgsqhkiG9w0BCRADCTAdBglghkgBZQMEASoEEDIQbJMC
6+Sfb3LpwHduj/meQEMKwrwq5M4V0stztm6OUTAsFY2zKDY20SApwSEeEcAh9TM42E
7+1palnHeqHTBpC8pIpjA8BgkqhkiG9w0BBwEwHQYJYIZIAWUDBAEqBBByt+scPrdM
8+giR7WUOJyB3hgBDcD3UDMtZSep8X/3yy1/Yq
9+-----END CMS-----
+12
examples/crl-rfc5280.b64
···000000000000
···1+CRL example from RFC5280 as found here:
2+https://csrc.nist.gov/projects/pki-testing/sample-certificates-and-crls
3+4+begin-base64 644 crl-rfc5280.der
5+MIIBYDCBygIBATANBgkqhkiG9w0BAQUFADBDMRMwEQYKCZImiZPyLGQBGRYDY29tMRcwFQYKCZIm
6+iZPyLGQBGRYHZXhhbXBsZTETMBEGA1UEAxMKRXhhbXBsZSBDQRcNMDUwMjA1MTIwMDAwWhcNMDUw
7+MjA2MTIwMDAwWjAiMCACARIXDTA0MTExOTE1NTcwM1owDDAKBgNVHRUEAwoBAaAvMC0wHwYDVR0j
8+BBgwFoAUCGivhTPIOUp6+IKTjnBqSiCELDIwCgYDVR0UBAMCAQwwDQYJKoZIhvcNAQEFBQADgYEA
9+ItwYffcIzsx10NBqm60Q9HYjtIFutW2+DvsVFGzIF20f7pAXom9g5L2qjFXejoRvkvifEBInr0rU
10+L4XiNkR9qqNMJTgV/wD9Pn7uPSYS69jnK2LiK8NGgO94gtEVxtCccmrLznrtZ5mLbnCBfUNCdMGm
11+r8FVF6IzTNYGmCuk/C4=
12+====
+8
examples/ldapmessage.b64
···00000000
···1+LDAPMessage example as found on ldap.com.
2+3+Original link:
4+https://ldap.com/ldapv3-wire-protocol-reference-ldap-message/
5+6+begin-base64 644 ldapmessage.der
7+MDUCAQVKEWRjPWV4YW1wbGUsZGM9Y29toB0wGwQWMS4yLjg0MC4xMTM1NTYuMS40LjgwNQEB/w==
8+====
···1// Hex JavaScript decoder
2-// Copyright (c) 2008-2021 Lapo Luchini <lapo@lapo.it>
34// Permission to use, copy, modify, and/or distribute this software for any
5// purpose with or without fee is hereby granted, provided that the above
6// copyright notice and this permission notice appear in all copies.
7-//
8// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
···13// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1516-(typeof define != 'undefined' ? define : function (factory) { 'use strict';
17- if (typeof module == 'object') module.exports = factory();
18- else window.hex = factory();
19-})(function () {
20-"use strict";
2122-var Hex = {},
23- decoder, // populated on first usage
24- haveU8 = (typeof Uint8Array == 'function');
2526-/**
27- * Decodes an hexadecimal value.
28- * @param {string|Array|Uint8Array} a - a string representing hexadecimal data, or an array representation of its charcodes
29- */
30-Hex.decode = function(a) {
31- var isString = (typeof a == 'string');
32- var i;
33- if (decoder === undefined) {
34- var hex = "0123456789ABCDEF",
35- ignore = " \f\n\r\t\u00A0\u2028\u2029";
36- decoder = [];
37- for (i = 0; i < 16; ++i)
38- decoder[hex.charCodeAt(i)] = i;
39- hex = hex.toLowerCase();
40- for (i = 10; i < 16; ++i)
41- decoder[hex.charCodeAt(i)] = i;
42- for (i = 0; i < ignore.length; ++i)
43- decoder[ignore.charCodeAt(i)] = -1;
44- }
45- var out = haveU8 ? new Uint8Array(a.length >> 1) : [],
46- bits = 0,
47- char_count = 0,
48- len = 0;
49- for (i = 0; i < a.length; ++i) {
50- var c = isString ? a.charCodeAt(i) : a[i];
51- c = decoder[c];
52- if (c == -1)
53- continue;
54- if (c === undefined)
55- throw 'Illegal character at offset ' + i;
56- bits |= c;
57- if (++char_count >= 2) {
58- out[len++] = bits;
59- bits = 0;
60- char_count = 0;
61- } else {
62- bits <<= 4;
063 }
0000064 }
65- if (char_count)
66- throw "Hex encoding incomplete: 4 bits missing";
67- if (haveU8 && out.length > len) // in case it was originally longer because of ignored characters
68- out = out.subarray(0, len);
69- return out;
70-};
7172-return Hex;
73-74-});
···1// Hex JavaScript decoder
2+// Copyright (c) 2008 Lapo Luchini <lapo@lapo.it>
34// Permission to use, copy, modify, and/or distribute this software for any
5// purpose with or without fee is hereby granted, provided that the above
6// copyright notice and this permission notice appear in all copies.
7+//
8// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
···13// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1516+const
17+ haveU8 = (typeof Uint8Array == 'function');
18+19+let decoder; // populated on first usage
02021+export class Hex {
002223+ /**
24+ * Decodes an hexadecimal value.
25+ * @param {string|Array|Uint8Array} a - a string representing hexadecimal data, or an array representation of its charcodes
26+ */
27+ static decode(a) {
28+ let isString = (typeof a == 'string');
29+ let i;
30+ if (decoder === undefined) {
31+ let hex = '0123456789ABCDEF',
32+ ignore = ' \f\n\r\t\u00A0\u2028\u2029';
33+ decoder = [];
34+ for (i = 0; i < 16; ++i)
35+ decoder[hex.charCodeAt(i)] = i;
36+ hex = hex.toLowerCase();
37+ for (i = 10; i < 16; ++i)
38+ decoder[hex.charCodeAt(i)] = i;
39+ for (i = 0; i < ignore.length; ++i)
40+ decoder[ignore.charCodeAt(i)] = -1;
41+ }
42+ let out = haveU8 ? new Uint8Array(a.length >> 1) : [],
43+ bits = 0,
44+ char_count = 0,
45+ len = 0;
46+ for (i = 0; i < a.length; ++i) {
47+ let c = isString ? a.charCodeAt(i) : a[i];
48+ c = decoder[c];
49+ if (c == -1)
50+ continue;
51+ if (c === undefined)
52+ throw 'Illegal character at offset ' + i;
53+ bits |= c;
54+ if (++char_count >= 2) {
55+ out[len++] = bits;
56+ bits = 0;
57+ char_count = 0;
58+ } else {
59+ bits <<= 4;
60+ }
61 }
62+ if (char_count)
63+ throw 'Hex encoding incomplete: 4 bits missing';
64+ if (haveU8 && out.length > len) // in case it was originally longer because of ignored characters
65+ out = out.subarray(0, len);
66+ return out;
67 }
0000006869+}
00
···1-(typeof define != 'undefined' ? define : function (factory) { 'use strict';
2- if (typeof module == 'object') factory(function (name) { return require(name); });
3- else factory(function (name) { return window[name.substring(2)]; });
4-})(function (require) {
5-"use strict";
067-var ASN1 = require('./asn1'),
8- Base64 = require('./base64'),
9- Hex = require('./hex'),
10 maxLength = 10240,
11 reHex = /^\s*(?:[0-9A-Fa-f][0-9A-Fa-f]\s*)+$/,
12 tree = id('tree'),
13 dump = id('dump'),
14- wantHex = id('wantHex'),
0015 area = id('area'),
16 file = id('file'),
17 examples = id('examples'),
18- hash = null;
01920-require('./dom'); // side effect: augment ASN1
021if (!window.console || !window.console.log) // IE8 with closed developer tools
22 window.console = { log: function () {} };
23function id(elem) {
24 return document.getElementById(elem);
25}
26function text(el, string) {
27- if ('textContent' in el)
28- el.textContent = string;
29- else
30- el.innerText = string;
31}
32-function decode(der, offset) {
33- offset = offset || 0;
000000034 tree.innerHTML = '';
35 dump.innerHTML = '';
0000000036 try {
37- var asn1 = ASN1.decode(der, offset);
38- tree.appendChild(asn1.toDOM());
39- if (wantHex.checked)
40- dump.appendChild(asn1.toHexDOM());
41- var b64 = (der.length < maxLength) ? asn1.toB64String() : '';
42- if (area.value === '')
43- area.value = Base64.pretty(b64);
00000000000000000000000000044 try {
45 window.location.hash = hash = '#' + b64;
46- } catch (e) { // fails with "Access Denied" on IE with URLs longer than ~2048 chars
047 window.location.hash = hash = '#';
48 }
49- var endOffset = asn1.posEnd();
50 if (endOffset < der.length) {
51- var p = document.createElement('p');
52 p.innerText = 'Input contains ' + (der.length - endOffset) + ' more bytes to decode.';
53- var button = document.createElement('button');
54 button.innerText = 'try to decode';
55 button.onclick = function () {
56 decode(der, endOffset);
···62 text(tree, e);
63 }
64}
65-function decodeText(val) {
66 try {
67- var der = reHex.test(val) ? Hex.decode(val) : Base64.unarmor(val);
68 decode(der);
69 } catch (e) {
70 text(tree, e);
71 dump.innerHTML = '';
72 }
73}
74-function decodeBinaryString(str) {
75- var der;
76 try {
77- if (reHex.test(str))
78- der = Hex.decode(str);
79- else if (Base64.re.test(str))
80- der = Base64.unarmor(str);
81- else
82- der = str;
83 decode(der);
84 } catch (e) {
85 text(tree, 'Cannot decode file.');
···87 }
88}
89// set up buttons
90-id('butDecode').onclick = function () { decodeText(area.value); };
91-id('butClear').onclick = function () {
92- area.value = '';
93- file.value = '';
94- tree.innerHTML = '';
95- dump.innerHTML = '';
96- hash = window.location.hash = '';
97-};
98-id('butExample').onclick = function () {
99- console.log('Loading example:', examples.value);
100- var request = new XMLHttpRequest();
101- request.open('GET', 'examples/' + examples.value, true);
102- request.onreadystatechange = function() {
103- if (this.readyState !== 4)
104- return;
105- if (this.status >= 200 && this.status < 400) {
106- area.value = this.responseText;
107- decodeText(this.responseText);
108- } else {
109- console.log('Error loading example.');
110- }
111- };
112- request.send();
0000113};
00000114// this is only used if window.FileReader
115function read(f) {
116 area.value = ''; // clear text area, will get b64 content
117- var r = new FileReader();
118 r.onloadend = function () {
119- if (r.error)
120- alert("Your browser couldn't read the specified file (error code " + r.error.code + ").");
121- else
122- decodeBinaryString(r.result);
123 };
124 r.readAsBinaryString(f);
125}
126function load() {
127- if (file.files.length === 0)
128- alert("Select a file to load first.");
129- else
130- read(file.files[0]);
131}
132function loadFromHash() {
133 if (window.location.hash && window.location.hash != hash) {
···135 // Firefox is not consistent with other browsers and returns an
136 // already-decoded hash string so we risk double-decoding here,
137 // but since % is not allowed in base64 nor hexadecimal, it's ok
138- var val = decodeURIComponent(hash.substr(1));
139- if (val.length)
140- decodeText(val);
141 }
142}
143function stop(e) {
···146}
147function dragAccept(e) {
148 stop(e);
149- if (e.dataTransfer.files.length > 0)
150- read(e.dataTransfer.files[0]);
151}
152// main
153-if ('onhashchange' in window)
154- window.onhashchange = loadFromHash;
155loadFromHash();
156document.ondragover = stop;
157document.ondragleave = stop;
158-if ('FileReader' in window && 'readAsBinaryString' in (new FileReader())) {
159 file.style.display = 'block';
160 file.onchange = load;
161 document.ondrop = dragAccept;
162}
163-164-});
000000000
···1+import './theme.js';
2+import { ASN1DOM } from './dom.js';
3+import { Base64 } from './base64.js';
4+import { Hex } from './hex.js';
5+import { Defs } from './defs.js';
6+import { tags } from './tags.js';
78+const
009 maxLength = 10240,
10 reHex = /^\s*(?:[0-9A-Fa-f][0-9A-Fa-f]\s*)+$/,
11 tree = id('tree'),
12 dump = id('dump'),
13+ wantHex = checkbox('wantHex'),
14+ trimHex = checkbox('trimHex'),
15+ wantDef = checkbox('wantDef'),
16 area = id('area'),
17 file = id('file'),
18 examples = id('examples'),
19+ selectDefs = id('definitions'),
20+ selectTag = id('tags');
2122+let hash = null;
23+24if (!window.console || !window.console.log) // IE8 with closed developer tools
25 window.console = { log: function () {} };
26function id(elem) {
27 return document.getElementById(elem);
28}
29function text(el, string) {
30+ if ('textContent' in el) el.textContent = string;
31+ else el.innerText = string;
0032}
33+function checkbox(name) {
34+ const el = id(name);
35+ const cfg = localStorage.getItem(name);
36+ if (cfg === 'false')
37+ el.checked = false;
38+ el.onchange = () => localStorage.setItem(name, el.checked);
39+ return el;
40+}
41+function show(asn1) {
42 tree.innerHTML = '';
43 dump.innerHTML = '';
44+ let ul = document.createElement('ul');
45+ ul.className = 'treecollapse';
46+ tree.appendChild(ul);
47+ ul.appendChild(asn1.toDOM());
48+ if (wantHex.checked) dump.appendChild(asn1.toHexDOM(undefined, trimHex.checked));
49+}
50+export function decode(der, offset) {
51+ offset = offset || 0;
52 try {
53+ const asn1 = ASN1DOM.decode(der, offset);
54+ if (wantDef.checked) {
55+ selectDefs.innerHTML = '';
56+ const types = Defs.commonTypes
57+ .map(type => {
58+ const stats = Defs.match(asn1, type);
59+ return { type, match: stats.recognized / stats.total };
60+ })
61+ .sort((a, b) => b.match - a.match);
62+ for (const t of types) {
63+ t.element = document.createElement('option');
64+ t.element.innerText = (t.match * 100).toFixed(1) + '% ' + t.type.description;
65+ selectDefs.appendChild(t.element);
66+ }
67+ let not = document.createElement('option');
68+ not.innerText = 'no definition';
69+ selectDefs.appendChild(not);
70+ Defs.match(asn1, types[0].type);
71+ selectDefs.onchange = () => {
72+ for (const t of types) {
73+ if (t.element == selectDefs.selectedOptions[0]) {
74+ Defs.match(asn1, t.type);
75+ show(asn1);
76+ return;
77+ }
78+ }
79+ Defs.match(asn1, null);
80+ show(asn1);
81+ };
82+ } else
83+ selectDefs.innerHTML = '<option>no definition</option>';
84+ show(asn1);
85+ let b64 = der.length < maxLength ? asn1.toB64String() : '';
86+ if (area.value === '') area.value = Base64.pretty(b64);
87 try {
88 window.location.hash = hash = '#' + b64;
89+ } catch (e) {
90+ // fails with "Access Denied" on IE with URLs longer than ~2048 chars
91 window.location.hash = hash = '#';
92 }
93+ let endOffset = asn1.posEnd();
94 if (endOffset < der.length) {
95+ let p = document.createElement('p');
96 p.innerText = 'Input contains ' + (der.length - endOffset) + ' more bytes to decode.';
97+ let button = document.createElement('button');
98 button.innerText = 'try to decode';
99 button.onclick = function () {
100 decode(der, endOffset);
···106 text(tree, e);
107 }
108}
109+export function decodeText(val) {
110 try {
111+ let der = reHex.test(val) ? Hex.decode(val) : Base64.unarmor(val);
112 decode(der);
113 } catch (e) {
114 text(tree, e);
115 dump.innerHTML = '';
116 }
117}
118+export function decodeBinaryString(str) {
119+ let der;
120 try {
121+ if (reHex.test(str)) der = Hex.decode(str);
122+ else if (Base64.re.test(str)) der = Base64.unarmor(str);
123+ else der = str;
000124 decode(der);
125 } catch (e) {
126 text(tree, 'Cannot decode file.');
···128 }
129}
130// set up buttons
131+const butClickHandlers = {
132+ butDecode: () => {
133+ decodeText(area.value);
134+ },
135+ butClear: () => {
136+ area.value = '';
137+ file.value = '';
138+ tree.innerHTML = '';
139+ dump.innerHTML = '';
140+ selectDefs.innerHTML = '';
141+ hash = window.location.hash = '';
142+ },
143+ butExample: () => {
144+ console.log('Loading example:', examples.value);
145+ let request = new XMLHttpRequest();
146+ request.open('GET', 'examples/' + examples.value, true);
147+ request.onreadystatechange = function () {
148+ if (this.readyState !== 4) return;
149+ if (this.status >= 200 && this.status < 400) {
150+ area.value = this.responseText;
151+ decodeText(this.responseText);
152+ } else {
153+ console.log('Error loading example.');
154+ }
155+ };
156+ request.send();
157+ },
158};
159+for (const [name, onClick] of Object.entries(butClickHandlers)) {
160+ let elem = id(name);
161+ if (elem)
162+ elem.onclick = onClick;
163+}
164// this is only used if window.FileReader
165function read(f) {
166 area.value = ''; // clear text area, will get b64 content
167+ let r = new FileReader();
168 r.onloadend = function () {
169+ if (r.error) alert("Your browser couldn't read the specified file (error code " + r.error.code + ').');
170+ else decodeBinaryString(r.result);
00171 };
172 r.readAsBinaryString(f);
173}
174function load() {
175+ if (file.files.length === 0) alert('Select a file to load first.');
176+ else read(file.files[0]);
00177}
178function loadFromHash() {
179 if (window.location.hash && window.location.hash != hash) {
···181 // Firefox is not consistent with other browsers and returns an
182 // already-decoded hash string so we risk double-decoding here,
183 // but since % is not allowed in base64 nor hexadecimal, it's ok
184+ let val = decodeURIComponent(hash.substr(1));
185+ if (val.length) decodeText(val);
0186 }
187}
188function stop(e) {
···191}
192function dragAccept(e) {
193 stop(e);
194+ if (e.dataTransfer.files.length > 0) read(e.dataTransfer.files[0]);
0195}
196// main
197+if ('onhashchange' in window) window.onhashchange = loadFromHash;
0198loadFromHash();
199document.ondragover = stop;
200document.ondragleave = stop;
201+if ('FileReader' in window && 'readAsBinaryString' in new FileReader()) {
202 file.style.display = 'block';
203 file.onchange = load;
204 document.ondrop = dragAccept;
205}
206+for (let tag in tags) {
207+ let date = tags[tag];
208+ let el = document.createElement('option');
209+ el.value = tag;
210+ el.innerText = date + ' ' + tag;
211+ selectTag.appendChild(el);
212+}
213+selectTag.onchange = function (ev) {
214+ let tag = ev.target.selectedOptions[0].value;
215+ window.location.href = 'https://rawcdn.githack.com/lapo-luchini/asn1js/' + tag + '/index.html';
216+};
+89-90
int10.js
···1// Big integer base-10 printing library
2-// Copyright (c) 2008-2021 Lapo Luchini <lapo@lapo.it>
34// Permission to use, copy, modify, and/or distribute this software for any
5// purpose with or without fee is hereby granted, provided that the above
6// copyright notice and this permission notice appear in all copies.
7-//
8// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
···13// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1516-(typeof define != 'undefined' ? define : function (factory) { 'use strict';
17- if (typeof module == 'object') module.exports = factory();
18- else window.int10 = factory();
19-})(function () {
20-"use strict";
2122-var max = 10000000000000; // biggest 10^n integer that can still fit 2^53 when multiplied by 256
00000002324-/**
25- * Arbitrary length base-10 value.
26- * @param {number} value - Optional initial value (will be 0 otherwise).
27- */
28-function Int10(value) {
29- this.buf = [+value || 0];
30-}
0000000000000000003132-/**
33- * Multiply value by m and add c.
34- * @param {number} m - multiplier, must be < =256
35- * @param {number} c - value to add
36- */
37-Int10.prototype.mulAdd = function (m, c) {
38- // assert(m <= 256)
39- var b = this.buf,
40- l = b.length,
41- i, t;
42- for (i = 0; i < l; ++i) {
43- t = b[i] * m + c;
44- if (t < max)
45- c = 0;
46- else {
47- c = 0|(t / max);
48- t -= c * max;
49 }
50- b[i] = t;
051 }
52- if (c > 0)
53- b[i] = c;
54-};
5556-/**
57- * Subtract value.
58- * @param {number} c - value to subtract
59- */
60-Int10.prototype.sub = function (c) {
61- var b = this.buf,
62- l = b.length,
63- i, t;
64- for (i = 0; i < l; ++i) {
65- t = b[i] - c;
66- if (t < 0) {
67- t += max;
68- c = 1;
69- } else
70- c = 0;
71- b[i] = t;
72 }
73- while (b[b.length - 1] === 0)
74- b.pop();
75-};
7677-/**
78- * Convert to decimal string representation.
79- * @param {*} base - optional value, only value accepted is 10
80- */
81-Int10.prototype.toString = function (base) {
82- if ((base || 10) != 10)
83- throw 'only base 10 is supported';
84- var b = this.buf,
85- s = b[b.length - 1].toString();
86- for (var i = b.length - 2; i >= 0; --i)
87- s += (max + b[i]).toString().substring(1);
88- return s;
89-};
9091-/**
92- * Convert to Number value representation.
93- * Will probably overflow 2^53 and thus become approximate.
94- */
95-Int10.prototype.valueOf = function () {
96- var b = this.buf,
97- v = 0;
98- for (var i = b.length - 1; i >= 0; --i)
99- v = v * max + b[i];
100- return v;
101-};
102-103-/**
104- * Return value as a simple Number (if it is <= 10000000000000), or return this.
105- */
106-Int10.prototype.simplify = function () {
107- var b = this.buf;
108- return (b.length == 1) ? b[0] : this;
109-};
110-111-return Int10;
112113-});
···1// Big integer base-10 printing library
2+// Copyright (c) 2008 Lapo Luchini <lapo@lapo.it>
34// Permission to use, copy, modify, and/or distribute this software for any
5// purpose with or without fee is hereby granted, provided that the above
6// copyright notice and this permission notice appear in all copies.
7+//
8// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
···13// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1516+/** Biggest 10^n integer that can still fit 2^53 when multiplied by 256. */
17+const max = 10000000000000;
0001819+export class Int10 {
20+ /**
21+ * Arbitrary length base-10 value.
22+ * @param {number} value - Optional initial value (will be 0 otherwise).
23+ */
24+ constructor(value) {
25+ this.buf = [+value || 0];
26+ }
2728+ /**
29+ * Multiply value by m and add c.
30+ * @param {number} m - multiplier, must be 0<m<=256
31+ * @param {number} c - value to add, must be c>=0
32+ */
33+ mulAdd(m, c) {
34+ // assert(m > 0)
35+ // assert(m <= 256)
36+ // assert(c >= 0)
37+ let b = this.buf,
38+ l = b.length,
39+ i, t;
40+ for (i = 0; i < l; ++i) {
41+ t = b[i] * m + c;
42+ if (t < max)
43+ c = 0;
44+ else {
45+ c = 0|(t / max);
46+ t -= c * max;
47+ }
48+ b[i] = t;
49+ }
50+ if (c > 0)
51+ b[i] = c;
52+ }
5354+ /**
55+ * Subtract value.
56+ * @param {number} c - value to subtract
57+ */
58+ sub(c) {
59+ let b = this.buf,
60+ l = b.length,
61+ i, t;
62+ for (i = 0; i < l; ++i) {
63+ t = b[i] - c;
64+ if (t < 0) {
65+ t += max;
66+ c = 1;
67+ } else
68+ c = 0;
69+ b[i] = t;
070 }
71+ while (b[b.length - 1] === 0)
72+ b.pop();
73 }
0007475+ /**
76+ * Convert to decimal string representation.
77+ * @param {number} [base=10] - optional value, only value accepted is 10
78+ * @returns {string} The decimal string representation.
79+ */
80+ toString(base = 10) {
81+ if (base != 10)
82+ throw new Error('only base 10 is supported');
83+ let b = this.buf,
84+ s = b[b.length - 1].toString();
85+ for (let i = b.length - 2; i >= 0; --i)
86+ s += (max + b[i]).toString().substring(1);
87+ return s;
00088 }
0008990+ /**
91+ * Convert to Number value representation.
92+ * Will probably overflow 2^53 and thus become approximate.
93+ * @returns {number} The numeric value.
94+ */
95+ valueOf() {
96+ let b = this.buf,
97+ v = 0;
98+ for (let i = b.length - 1; i >= 0; --i)
99+ v = v * max + b[i];
100+ return v;
101+ }
0102103+ /**
104+ * Return value as a simple Number (if it is <= 10000000000000), or return this.
105+ * @returns {number | Int10} The simplified value.
106+ */
107+ simplify() {
108+ let b = this.buf;
109+ return (b.length == 1) ? b[0] : this;
110+ }
0000000000000111112+}
···1+#/bin/sh
2+URL='https://www.cs.auckland.ac.nz/~pgut001/dumpasn1.cfg'
3+if [ -x /usr/bin/fetch ]; then
4+ /usr/bin/fetch -m --no-verify-peer $URL
5+elif [ -x /usr/bin/wget ]; then
6+ /usr/bin/wget -N --no-check-certificate $URL
7+elif [ ! -r dumpasn1.cfg ]; then
8+ echo Please download $URL in this directory.
9+ exit 1
10+fi
11+cat dumpasn1.cfg | \
12+tr -d '\r' | \
13+awk -v apos="'" -v q='"' -v url="$URL" '
14+ function clean() {
15+ oid = "";
16+ comment = "";
17+ description = "";
18+ warning = "";
19+ }
20+ BEGIN {
21+ FS = "= *";
22+ clean();
23+ print "// Converted from: " url;
24+ print "// which is made by Peter Gutmann and whose license states:";
25+ print "// You can use this code in whatever way you want,";
26+ print "// as long as you don" apos "t try to claim you wrote it.";
27+ print "export const oids = {";
28+ }
29+ /^OID/ { oid = $2; }
30+ /^Comment/ { comment = $2; }
31+ /^Description/ { description = $2; }
32+ /^Warning/ { warning = ", \"w\": true"; }
33+ /^$/ {
34+ if (length(oid) > 0) {
35+ gsub(" ", ".", oid);
36+ gsub("\"", "\\\"", description);
37+ gsub("\"", "\\\"", comment);
38+ if (++seen[oid] > 1)
39+ print "Duplicate OID in line " NR ": " oid > "/dev/stderr";
40+ else
41+ printf "\"%s\": { \"d\": \"%s\", \"c\": \"%s\"%s },\n", oid, description, comment, warning;
42+ clean();
43+ }
44+ }
45+ END {
46+ print "};"
47+ }
48+' >oids.js
49+echo Conversion completed.
+32
updateRFC.sh
···00000000000000000000000000000000
···1+#/bin/sh
2+RFCs="5280 5208 3369 3161 2986 4211 4210 8017 4511"
3+downloadRFC() {
4+ URL="https://www.ietf.org/rfc/rfc$1.txt"
5+ if [ -x /usr/bin/fetch ]; then
6+ /usr/bin/fetch -m --no-verify-peer $URL
7+ elif [ -x /usr/bin/wget ]; then
8+ /usr/bin/wget -N --no-check-certificate $URL
9+ elif [ ! -r dumpasn1.cfg ]; then
10+ echo Please download $URL in this directory.
11+ exit 1
12+ fi
13+}
14+echo '{}' > rfcdef.json # start from scratch
15+mkdir -p rfc
16+cd rfc
17+for n in $RFCs; do
18+ downloadRFC $n
19+ ../parseRFC.js rfc$n.txt ../rfcdef.json
20+done
21+cd ..
22+{
23+ echo "// content parsed from ASN.1 definitions as found in the following RFCs: $RFCs"
24+ echo "// Copyright (C) The IETF Trust (2008)"
25+ echo "// as far as I can tell this file is allowed under the following clause:"
26+ echo "// It is acceptable under the current IETF rules (RFC 5378) to modify extracted code if necessary."
27+ echo "// https://trustee.ietf.org/about/faq/#reproducing-rfcs"
28+ echo -n "export const rfcdef = "
29+ cat rfcdef.json
30+ echo ";"
31+} > rfcdef.js
32+echo Conversion completed.