···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
+51-22
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-2023 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)
6969-- dark mode support added by [Oliver Burgmaier](https://github.com/olibu/)
9898+- dark mode and other UI improvements by [Oliver Burgmaier](https://github.com/olibu/)
7099- patches by [Nicolai Sรธborg](https://github.com/NicolaiSoeborg)
7110072101links
+89-39
asn1.js
···11// ASN.1 JavaScript decoder
22-// Copyright (c) 2008-2023 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
···2121 reTimeS = /^(\d\d)(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])([01]\d|2[0-3])(?:([0-5]\d)(?:([0-5]\d)(?:[.,](\d{1,3}))?)?)?(Z|(-(?:0\d|1[0-2])|[+](?:0\d|1[0-4]))([0-5]\d)?)?$/,
2222 reTimeL = /^(\d\d\d\d)(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])([01]\d|2[0-3])(?:([0-5]\d)(?:([0-5]\d)(?:[.,](\d{1,3}))?)?)?(Z|(-(?:0\d|1[0-2])|[+](?:0\d|1[0-4]))([0-5]\d)?)?$/,
2323 hexDigits = '0123456789ABCDEF',
2424- b64Safe = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_',
2424+ b64Std = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
2525+ b64URL = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_',
2526 tableT61 = [
2627 ['', ''],
2728 ['AEIOUaeiou', 'รรรรรร รจรฌรฒรน'], // Grave
···3839 ['', ''],
3940 ['OUou', 'ลลฐลลฑ'], // Double Acute
4041 ['AEIUaeiu', 'ฤฤฤฎลฒฤ ฤฤฏลณ'], // Ogonek
4141- ['CDELNRSTZcdelnrstz', 'ฤฤฤฤฝลลล ลคลฝฤฤฤฤพลลลกลฅลพ'] // Caron
4242+ ['CDELNRSTZcdelnrstz', 'ฤฤฤฤฝลลล ลคลฝฤฤฤฤพลลลกลฅลพ'], // Caron
4243 ];
43444445function stringCut(str, len) {
···5657 }
5758}
58595959-class Stream {
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 {
60636464+ /**
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+ */
6168 constructor(enc, pos) {
6269 if (enc instanceof Stream) {
6370 this.enc = enc.enc;
6471 this.pos = enc.pos;
6572 } else {
6666- // enc should be an array or a binary string
6773 this.enc = enc;
6874 this.pos = pos;
6975 }
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');
7084 }
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) */
7187 get(pos) {
7288 if (pos === undefined)
7389 pos = this.pos++;
7490 if (pos >= this.enc.length)
7575- throw 'Requesting byte offset ' + pos + ' on a stream of length ' + this.enc.length;
7676- return (typeof this.enc == 'string') ? this.enc.charCodeAt(pos) : this.enc[pos];
9191+ throw new Error('Requesting byte offset ' + pos + ' on a stream of length ' + this.enc.length);
9292+ return this.getRaw(pos);
7793 }
7878- hexByte(b) {
9494+ /** Convert a single byte to an hexadcimal string (of length 2).
9595+ * @param {number} b */
9696+ static hexByte(b) {
7997 return hexDigits.charAt((b >> 4) & 0xF) + hexDigits.charAt(b & 0xF);
8098 }
8181- hexDump(start, end, raw) {
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') {
82104 let s = '';
83105 for (let i = start; i < end; ++i) {
8484- s += this.hexByte(this.get(i));
8585- if (raw !== true)
106106+ if (type == 'byte' && i > start)
107107+ s += ' ';
108108+ s += Stream.hexByte(this.get(i));
109109+ if (type == 'dump')
86110 switch (i & 0xF) {
87111 case 0x7: s += ' '; break;
88112 case 0xF: s += '\n'; break;
···91115 }
92116 return s;
93117 }
9494- b64Dump(start, end) {
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;
95124 let extra = (end - start) % 3,
96125 s = '',
97126 i, c;
98127 for (i = start; i + 2 < end; i += 3) {
99128 c = this.get(i) << 16 | this.get(i + 1) << 8 | this.get(i + 2);
100100- s += b64Safe.charAt(c >> 18 & 0x3F);
101101- s += b64Safe.charAt(c >> 12 & 0x3F);
102102- s += b64Safe.charAt(c >> 6 & 0x3F);
103103- s += b64Safe.charAt(c & 0x3F);
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);
104133 }
105134 if (extra > 0) {
106135 c = this.get(i) << 16;
107136 if (extra > 1) c |= this.get(i + 1) << 8;
108108- s += b64Safe.charAt(c >> 18 & 0x3F);
109109- s += b64Safe.charAt(c >> 12 & 0x3F);
110110- if (extra == 2) s += b64Safe.charAt(c >> 6 & 0x3F);
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);
111141 }
112142 return s;
113143 }
···190220 let s = this.parseStringISO(start, end).str,
191221 m = (shortYear ? reTimeS : reTimeL).exec(s);
192222 if (!m)
193193- return 'Unrecognized time: ' + s;
223223+ throw new Error('Unrecognized time: ' + s);
194224 if (shortYear) {
195225 // to avoid querying the timer, use the fixed range [1970, 2069]
196226 // it will conform with ITU X.400 [-10, +40] sliding window until 2030
···245275 parseBitString(start, end, maxLength) {
246276 let unusedBits = this.get(start);
247277 if (unusedBits > 7)
248248- throw 'Invalid BitString with unusedBits=' + unusedBits;
278278+ throw new Error('Invalid BitString with unusedBits=' + unusedBits);
249279 let lenBit = ((end - start - 1) << 3) - unusedBits,
250280 s = '';
251281 for (let i = start + 1; i < end; ++i) {
···273303 end = start + maxLength;
274304 s = '';
275305 for (let i = start; i < end; ++i)
276276- s += this.hexByte(this.get(i));
306306+ s += Stream.hexByte(this.get(i));
277307 if (len > maxLength)
278308 s += ellipsis;
279309 return { size: len, str: s };
···368398369399export class ASN1 {
370400 constructor(stream, header, length, tag, tagLen, sub) {
371371- if (!(tag instanceof ASN1Tag)) throw 'Invalid tag value.';
401401+ if (!(tag instanceof ASN1Tag)) throw new Error('Invalid tag value.');
372402 this.stream = stream;
373403 this.header = header;
374404 this.length = length;
···485515 }
486516 toPrettyString(indent) {
487517 if (indent === undefined) indent = '';
488488- let s = indent + this.typeName() + ' @' + this.stream.pos;
518518+ let s = indent;
519519+ if (this.def) {
520520+ if (this.def.id)
521521+ s += this.def.id + ' ';
522522+ if (this.def.name && this.def.name != this.typeName().replace(/_/g, ' '))
523523+ s+= this.def.name + ' ';
524524+ if (this.def.mismatch)
525525+ s += '[?] ';
526526+ }
527527+ s += this.typeName() + ' @' + this.stream.pos;
489528 if (this.length >= 0)
490529 s += '+';
491530 s += this.length;
···517556 posLen() {
518557 return this.stream.pos + this.tagLen;
519558 }
520520- toHexString() {
521521- return this.stream.hexDump(this.posStart(), this.posEnd(), true);
559559+ /** Hexadecimal dump of the node.
560560+ * @param type 'raw', 'byte' or 'dump' */
561561+ toHexString(type = 'raw') {
562562+ return this.stream.hexDump(this.posStart(), this.posEnd(), type);
522563 }
523523- toB64String() {
524524- return this.stream.b64Dump(this.posStart(), this.posEnd());
564564+ /** Base64url dump of the node (according to RFC 4648 section 5).
565565+ * @param {string} type 'url' (default, section 5 without padding) or 'std' (section 4 with padding)
566566+ */
567567+ toB64String(type = 'url') {
568568+ return this.stream.b64Dump(this.posStart(), this.posEnd(), type);
525569 }
526570 static decodeLength(stream) {
527571 let buf = stream.get(),
···531575 if (len === 0) // long form with length 0 is a special case
532576 return null; // undefined length
533577 if (len > 6) // no reason to use Int10, as it would be a huge buffer anyways
534534- throw 'Length over 48 bits not supported at position ' + (stream.pos - 1);
578578+ throw new Error('Length over 48 bits not supported at position ' + (stream.pos - 1));
535579 buf = 0;
536580 for (let i = 0; i < len; ++i)
537581 buf = (buf * 256) + stream.get();
···539583 }
540584 static decode(stream, offset, type = ASN1) {
541585 if (!(type == ASN1 || type.prototype instanceof ASN1))
542542- throw 'Must pass a class that extends ASN1';
586586+ throw new Error('Must pass a class that extends ASN1');
543587 if (!(stream instanceof Stream))
544588 stream = new Stream(stream, offset || 0);
545589 let streamStart = new Stream(stream),
···555599 // definite length
556600 let end = start + len;
557601 if (end > stream.enc.length)
558558- throw 'Container at offset ' + start + ' has a length of ' + len + ', which is past the end of the stream';
602602+ throw new Error('Container at offset ' + start + ' has a length of ' + len + ', which is past the end of the stream');
559603 while (stream.pos < end)
560604 sub[sub.length] = type.decode(stream);
561605 if (stream.pos != end)
562562- throw 'Content size is not correct for container at offset ' + start;
606606+ throw new Error('Content size is not correct for container at offset ' + start);
563607 } else {
564608 // undefined length
565609 try {
···571615 }
572616 len = start - stream.pos; // undefined lengths are represented as negative values
573617 } catch (e) {
574574- throw 'Exception while decoding undefined length content at offset ' + start + ': ' + e;
618618+ throw new Error('Exception while decoding undefined length content at offset ' + start + ': ' + e);
575619 }
576620 }
577621 };
···583627 try {
584628 if (tag.tagNumber == 0x03)
585629 if (stream.get() != 0)
586586- throw 'BIT STRINGs with unused bits cannot encapsulate.';
630630+ throw new Error('BIT STRINGs with unused bits cannot encapsulate.');
587631 getSub();
588588- for (let i = 0; i < sub.length; ++i)
589589- if (sub[i].tag.isEOC())
590590- throw 'EOC is not supposed to be actual content.';
632632+ for (let s of sub) {
633633+ if (s.tag.isEOC())
634634+ throw new Error('EOC is not supposed to be actual content.');
635635+ try {
636636+ s.content();
637637+ } catch (e) {
638638+ throw new Error('Unable to parse content: ' + e);
639639+ }
640640+ }
591641 } catch (e) {
592642 // but silently ignore when they don't
593643 sub = null;
···596646 }
597647 if (sub === null) {
598648 if (len === null)
599599- throw "We can't skip over an invalid tag with undefined length at offset " + start;
649649+ throw new Error("We can't skip over an invalid tag with undefined length at offset " + start);
600650 stream.pos = start + Math.abs(len);
601651 }
602652 return new type(streamStart, header, len, tag, tagLen, sub);
+8-7
base64.js
···11// Base64 JavaScript decoder
22-// Copyright (c) 2008-2023 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
···3131 decoder[b64.charCodeAt(i)] = i;
3232 for (i = 0; i < ignore.length; ++i)
3333 decoder[ignore.charCodeAt(i)] = -1;
3434- // RFC 3548 URL & file safe encoding
3434+ // also support decoding Base64url (RFC 4648 section 5)
3535 decoder['-'.charCodeAt(0)] = decoder['+'.charCodeAt(0)];
3636 decoder['_'.charCodeAt(0)] = decoder['/'.charCodeAt(0)];
3737 }
···75757676 static pretty(str) {
7777 // fix padding
7878- if (str.length % 4 > 0)
7979- str = (str + '===').slice(0, str.length + str.length % 4);
8080- // convert RFC 3548 to standard Base64
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)
8182 str = str.replace(/-/g, '+').replace(/_/g, '/');
8283 // 80 column width
8383- return str.replace(/(.{80})/g, '$1\n');
8484+ return str.replace(/.{80}/g, '$&\n');
8485 }
85868687 static unarmor(a) {
···11+This is a PKCS#7/CMS encrypted with passwod.
22+$ echo content | openssl cms -encrypt -pwri_password test -aes256 -outform pem -out examples/cms-password.p7m
33+-----BEGIN CMS-----
44+MIHYBgkqhkiG9w0BBwOggcowgccCAQMxgYOjgYACAQCgGwYJKoZIhvcNAQUMMA4E
55+CED/DSxXMtH6AgIIADAsBgsqhkiG9w0BCRADCTAdBglghkgBZQMEASoEEDIQbJMC
66+Sfb3LpwHduj/meQEMKwrwq5M4V0stztm6OUTAsFY2zKDY20SApwSEeEcAh9TM42E
77+1palnHeqHTBpC8pIpjA8BgkqhkiG9w0BBwEwHQYJYIZIAWUDBAEqBBByt+scPrdM
88+giR7WUOJyB3hgBDcD3UDMtZSep8X/3yy1/Yq
99+-----END CMS-----
+12
examples/crl-rfc5280.b64
···11+CRL example from RFC5280 as found here:
22+https://csrc.nist.gov/projects/pki-testing/sample-certificates-and-crls
33+44+begin-base64 644 crl-rfc5280.der
55+MIIBYDCBygIBATANBgkqhkiG9w0BAQUFADBDMRMwEQYKCZImiZPyLGQBGRYDY29tMRcwFQYKCZIm
66+iZPyLGQBGRYHZXhhbXBsZTETMBEGA1UEAxMKRXhhbXBsZSBDQRcNMDUwMjA1MTIwMDAwWhcNMDUw
77+MjA2MTIwMDAwWjAiMCACARIXDTA0MTExOTE1NTcwM1owDDAKBgNVHRUEAwoBAaAvMC0wHwYDVR0j
88+BBgwFoAUCGivhTPIOUp6+IKTjnBqSiCELDIwCgYDVR0UBAMCAQwwDQYJKoZIhvcNAQEFBQADgYEA
99+ItwYffcIzsx10NBqm60Q9HYjtIFutW2+DvsVFGzIF20f7pAXom9g5L2qjFXejoRvkvifEBInr0rU
1010+L4XiNkR9qqNMJTgV/wD9Pn7uPSYS69jnK2LiK8NGgO94gtEVxtCccmrLznrtZ5mLbnCBfUNCdMGm
1111+r8FVF6IzTNYGmCuk/C4=
1212+====
+8
examples/ldapmessage.b64
···11+LDAPMessage example as found on ldap.com.
22+33+Original link:
44+https://ldap.com/ldapv3-wire-protocol-reference-ldap-message/
55+66+begin-base64 644 ldapmessage.der
77+MDUCAQVKEWRjPWV4YW1wbGUsZGM9Y29toB0wGwQWMS4yLjg0MC4xMTM1NTYuMS40LjgwNQEB/w==
88+====
···11// Hex JavaScript decoder
22-// Copyright (c) 2008-2023 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
···11+import './theme.js';
12import { ASN1DOM } from './dom.js';
23import { Base64 } from './base64.js';
34import { Hex } from './hex.js';
···1516 area = id('area'),
1617 file = id('file'),
1718 examples = id('examples'),
1818- selectTheme = id('theme-select'),
1919 selectDefs = id('definitions'),
2020 selectTag = id('tags');
2121···4141function show(asn1) {
4242 tree.innerHTML = '';
4343 dump.innerHTML = '';
4444- tree.appendChild(asn1.toDOM());
4444+ let ul = document.createElement('ul');
4545+ ul.className = 'treecollapse';
4646+ tree.appendChild(ul);
4747+ ul.appendChild(asn1.toDOM());
4548 if (wantHex.checked) dump.appendChild(asn1.toHexDOM(undefined, trimHex.checked));
4649}
4747-function decode(der, offset) {
5050+export function decode(der, offset) {
4851 offset = offset || 0;
4952 try {
5053 const asn1 = ASN1DOM.decode(der, offset);
···103106 text(tree, e);
104107 }
105108}
106106-function decodeText(val) {
109109+export function decodeText(val) {
107110 try {
108111 let der = reHex.test(val) ? Hex.decode(val) : Base64.unarmor(val);
109112 decode(der);
···112115 dump.innerHTML = '';
113116 }
114117}
115115-function decodeBinaryString(str) {
118118+export function decodeBinaryString(str) {
116119 let der;
117120 try {
118121 if (reHex.test(str)) der = Hex.decode(str);
···125128 }
126129}
127130// set up buttons
128128-id('butDecode').onclick = function () {
129129- decodeText(area.value);
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+ },
130158};
131131-id('butClear').onclick = function () {
132132- area.value = '';
133133- file.value = '';
134134- tree.innerHTML = '';
135135- dump.innerHTML = '';
136136- selectDefs.innerHTML = '';
137137- hash = window.location.hash = '';
138138-};
139139-id('butExample').onclick = function () {
140140- console.log('Loading example:', examples.value);
141141- let request = new XMLHttpRequest();
142142- request.open('GET', 'examples/' + examples.value, true);
143143- request.onreadystatechange = function () {
144144- if (this.readyState !== 4) return;
145145- if (this.status >= 200 && this.status < 400) {
146146- area.value = this.responseText;
147147- decodeText(this.responseText);
148148- } else {
149149- console.log('Error loading example.');
150150- }
151151- };
152152- request.send();
153153-};
154154-// set dark theme depending on OS settings
155155-function setTheme() {
156156- let storedTheme = localStorage.getItem('theme');
157157- let theme = 'os';
158158- if (storedTheme)
159159- theme = storedTheme;
160160- selectTheme.value = theme;
161161- if (theme == 'os') {
162162- let prefersDarkScheme = window.matchMedia('(prefers-color-scheme: dark)');
163163- theme = prefersDarkScheme.matches ? 'dark': 'light';
164164- }
165165- if (theme == 'dark') {
166166- const css1 = id('theme-base');
167167- const css2 = css1.cloneNode();
168168- css2.id = 'theme-override';
169169- css2.href = 'index-' + theme + '.css';
170170- css1.parentElement.appendChild(css2);
171171- } else {
172172- const css2 = id('theme-override');
173173- if (css2) css2.remove();
174174- }
159159+for (const [name, onClick] of Object.entries(butClickHandlers)) {
160160+ let elem = id(name);
161161+ if (elem)
162162+ elem.onclick = onClick;
175163}
176176-setTheme();
177177-selectTheme.addEventListener('change', function () {
178178- localStorage.setItem('theme', selectTheme.value);
179179- setTheme();
180180-});
181164// this is only used if window.FileReader
182165function read(f) {
183166 area.value = ''; // clear text area, will get b64 content
+15-9
int10.js
···11// Big integer base-10 printing library
22-// Copyright (c) 2008-2023 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-let max = 10000000000000; // biggest 10^n integer that can still fit 2^53 when multiplied by 256
1616+/** Biggest 10^n integer that can still fit 2^53 when multiplied by 256. */
1717+const max = 10000000000000;
17181819export class Int10 {
1920 /**
···26272728 /**
2829 * Multiply value by m and add c.
2929- * @param {number} m - multiplier, must be < =256
3030- * @param {number} c - value to add
3030+ * @param {number} m - multiplier, must be 0<m<=256
3131+ * @param {number} c - value to add, must be c>=0
3132 */
3233 mulAdd(m, c) {
3434+ // assert(m > 0)
3335 // assert(m <= 256)
3636+ // assert(c >= 0)
3437 let b = this.buf,
3538 l = b.length,
3639 i, t;
···71747275 /**
7376 * Convert to decimal string representation.
7474- * @param {*} base - optional value, only value accepted is 10
7777+ * @param {number} [base=10] - optional value, only value accepted is 10
7878+ * @returns {string} The decimal string representation.
7579 */
7676- toString(base) {
7777- if ((base || 10) != 10)
7878- throw 'only base 10 is supported';
8080+ toString(base = 10) {
8181+ if (base != 10)
8282+ throw new Error('only base 10 is supported');
7983 let b = this.buf,
8084 s = b[b.length - 1].toString();
8185 for (let i = b.length - 2; i >= 0; --i)
···8690 /**
8791 * Convert to Number value representation.
8892 * Will probably overflow 2^53 and thus become approximate.
9393+ * @returns {number} The numeric value.
8994 */
9095 valueOf() {
9196 let b = this.buf,
···9710298103 /**
99104 * Return value as a simple Number (if it is <= 10000000000000), or return this.
105105+ * @returns {number | Int10} The simplified value.
100106 */
101107 simplify() {
102108 let b = this.buf;
-1
oids.js
···27222722"1.3.6.1.4.1.40869.1.1.22.3": { "d": "TWCA EV policy", "c": "TWCA Root Certification Authority" },
27232723"2.16.840.1.113733.1.7.23.6": { "d": "VeriSign EV policy", "c": "VeriSign Class 3 Public Primary Certification Authority" },
27242724"2.16.840.1.114171.500.9": { "d": "Wells Fargo EV policy", "c": "Wells Fargo WellsSecure Public Root Certificate Authority" },
27252725-"END": ""
27262725};