Skip to content

Commit

Permalink
Squashed 'asn1decode/asn1js/' changes from 502ccfe..1383174
Browse files Browse the repository at this point in the history
1383174 Merge pull request #1 from lapo-luchini/trunk
88cb856 propagate from branch 'it.lapo.asn1js.github-87' (head 1c8c4956cd564008f7eed9da4b4c5339c41877ab)             to branch 'it.lapo.asn1js' (head 9f75cfa39a7b636c5f645b78d7c1ead1ca738473)
440b6c9 Recover dark theme changes from GitHub ea3c28fdcc467f2f8c20dd97747e27f3add2605b
1a42055 Use className/classList.
7e187c8 Drop empty lines.
28d859d `btnHideTree` is no longer needed.
f4df0c8 Zoom fix via css media query instead of JS
85c143f Drop overridden dependency. (else `npm` is broken; this way `pnpm` has a warning but still works)
e1b6d4d `npx svgo *.svg` (except "src" favicon, that could need to be edited)
dc0eeba Fix dark collapse icon.
0d4d50a Use a code style closer to trunk branch.
4c1706f propagate from branch 'it.lapo.asn1js' (head 2836ee93ead827f06d611376950b3de3012c96e1)             to branch 'it.lapo.asn1js.github-87' (head e3919c3c3e940015d73441c4379c5337ed084457)
f2a2ddd Forbid trailing spaces.
f03d403 propagate from branch 'it.lapo.asn1js.github-90' (head 82116fdcf1c3b63bcde16e03867bdcad1eb2d051)             to branch 'it.lapo.asn1js' (head cc6d74d0a3dc372a270fac7069edd9c55bd4cac9)
9a0a01e Small theme refactoring.
3fd828a Import module natively, not in the HTML.
c921739 Fix peer dependencies.
06b6f3c theme.js added to lint
37883bf theme-color set for iOS devices
041d7b6 Theme support for single file
844d85b Theme moved into own js file as of performance issues
c1554d2 Common css instead of css replacement
6c01961 Update tags.
a490f08 Version 2.0.4.
5930b5b Add CLI binary.
ea72cc2 Improve single-file mode.
8e1cfed Add links.
aba062a Update tags.
06d6fcc Add instruction for local usage.
51fb9c5 propagate from branch 'it.lapo.asn1js.esm' (head 38124bce4601268d8065c1abd3114977330f32c9)             to branch 'it.lapo.asn1js' (head 5ce4e6aff2b2fefa6f9db72f33d8632fb6959607)
c4b53e5 Version 2.0.3.
a71445e Add defs to npm package. Linting.
5caf633 merge of '7612a59a7e8328fb3cdd183616a8455c779d2aa2'      and 'a773502d82c5ca9f65a9d2026a029ad908bcd34c'
8e1cc32 Update require-ESM docs.
7a4d218 Improve ESM usage examples.
af73d5d Fix nodejs example usage.
260a7a5 Drop dead code.
b7e8538 Force favicon as dataURI in "local" file.
f20564c eslint.
9679f24 Add `pnpm` support. (why doesn't it support `npm` override format too?)
e6a247e Update ignore files.
e3dd726 Advertise now single-file HTML.
5cda4f7 Since ESM is not useable on `file:` protocol, use Vite to generate a single-file `index-local.html`.
f4fc630 Only associate buttons' `onClick` when they exist. (to allow removing them)
8dc103c Only install when needed by `lint`.
ee8643f Install listed `eslint` to avoid auto-installing a `9.x` version which has breaking changes.
76b5048 Squashed rebase of changes from lapo-luchini/asn1js#87
f2f3e2a Export decode functions. (needed by VSCode extension)
826f20c Add encrypted file example.
7cec5a1 Update tags.
6ae6072 Version 2.0.2.
6f4b911 Improve optional type matching.
89a776d Fix `NULL` support further.
57b9ce4 `NULL` is a built-in type.
7a871a8 Upgrade actions from deprecated Node 16 to 20.
5e7deb8 Install listed `eslint` to avoid auto-installing `9.1.0` which breaks with our configuration format.
fd1b6d0 Add new test to check for defs regressions.
8400fa7 Allow specifying expected type.
3f5e5fa Add support for data URI input.
20c1581 propagate from branch 'it.lapo.asn1js' (head 2af3ae813887be59bf3e2e123a12e77592921fa8)             to branch 'it.lapo.asn1js.esm' (head 95bda12bb582ab39529f02626bab93554a4dd214)
6fa69ff Add X.509 SubjectPublicKeyInfo to default types.
3bc6092 Add missing types to RFC parser.
6052316 Improve hover highlight.
b6c0059 Use `classList` instead of manipulating `className`.
bbd561f Fix sha256sums.
ce7a05a Lint new file too.
513085b propagate from branch 'it.lapo.asn1js' (head 2c2471b462a45fe5bbd48c9d9347f2df5ab039d9)             to branch 'it.lapo.asn1js.esm' (head 8fc43cda40cb66f028d050df54b1e506736d094a)
ad3d920 `eslint --fix`
cce28c5 Print defs also in pretty tring mode.
dc57878 Drop old forgotten "collapse" icon.
e41f826 Add context menu also on the tree on the left.
270561b Rename context menu values. Add B64. Put optional one last to keep button positions.
5908ead Avoid inline javascript. (for future usage with CSP header)
4a03d93 Refactor context menu to use existing methods. (and avoid re-parsing parsed data)
fe04d97 Use blocks instead of `<br>` in context menu.
0eb4fcf Improve hex dump capabilities of ASN.1 nodes.
3291c10 File forgot in previous (refactor) commit.
0c2dc0c Refactor contextual menu in a different module.
c2ebbf4 propagate from branch 'it.lapo.asn1js.esm' (head 1f81e568f0723f8ce942b435f81d205e157789ce)             to branch 'it.lapo.asn1js.github-82' (head 7107a48abf688173dcdf7bd716d415c8edd85f46)
e3fca65 Update credits.
be8d97a propagate from branch 'it.lapo.asn1js' (head dd18afd1c4344811526fc928f3975fe414584018)             to branch 'it.lapo.asn1js.github-82' (head 27ed876d23a39e4ec2bfd2f6233f38610720add2)
1fcc683 Force examples permissions.
aa1f1c3 propagate from branch 'it.lapo.asn1js' (head 741a5e9cfd4ef4e45f1e5b505e02c4142e82dd8d)             to branch 'it.lapo.asn1js.esm' (head 4d3ff39de9922b95cbd11c2534c9188ff1db72f0)
2654e9c Add very basic PKCS#1 support and RSA key examples.
5027c72 propagate from branch 'it.lapo.asn1js' (head a9524436000d6561058ba0ae8e2a58a6c49837fd)             to branch 'it.lapo.asn1js.esm' (head a6b77044e2cf0f91cb663d9d1fbfbca7f66c2d21)
8f048ac When trying to parse encapsulated values, check that the content can be parsed as well.
405c036 Drop dead code.
7a51b8a Fix tests.
26d3a72 Throw exceptions, not strings.
9ff23e1 Use HTTPS links.
c894e6a Index only needs to import a single module.
8663924 Version 2.0.1.
70426d2 Update tags.
65c222d Update browser support warnings.
b7f47f6 Improve README.
f220f66 propagate from branch 'it.lapo.asn1js' (head 263fa0cb3936ec99c2be2c51a2265cadfbded687)             to branch 'it.lapo.asn1js.esm' (head c360c489896f2e9ec2c280e6d55b03a0581252c2)
690364a Improve type matching.
ae4458e Update example usage.
1b3078e Version 2.0.0 in order to signal this is a breaking change.
ae7ee7b Unbreak tests.
72c71f1 Update copyright.
c1f4dff Convert to ES6 Modules (ESM).
429f752 Indention fixed
1d7f90b Fix: Show context menu at correct position
e6dbc08 Negative list for set and sequence
dcf1b13 New: Align context menu to left
af5e9e7 New: Copy as pretty
d5465fc Fix: Copy the string to clipboard
e7d2bfd Context menu initially hidden
17dd53a Context menu and copyAsString added
c7cf816 hex copy on click
7c2a63b Update `eslint` rules a bit.

git-subtree-dir: asn1decode/asn1js
git-subtree-split: 138317433032d91debc2eea51536beadb757835d
  • Loading branch information
Christof Rath committed Sep 3, 2024
1 parent f48a2be commit 240f82c
Show file tree
Hide file tree
Showing 36 changed files with 1,350 additions and 466 deletions.
8 changes: 5 additions & 3 deletions .github/workflows/node.js.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,16 @@ jobs:

strategy:
matrix:
node-version: [ 6.4.0, latest ]
node-version: [ 12.20.0, latest ]

steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: npm test all
- run: npm install
if: matrix.node-version == 'latest'
- run: npm run lint
if: matrix.node-version == 'latest'
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ _MTN/
.DS_Store/
.vscode/
node_modules/
dist/
package-lock.json
pnpm-lock.yaml
# Artifacts from release.sh
index-local.html
sha256sums.asc
asn1js.zip
# Artifacts from mirror_to_github.sh
Expand Down
2 changes: 2 additions & 0 deletions .mtn-ignore
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
[.]DS_Store$
[.]vscode$
node_modules$
dist$
package-lock[.]json
pnpm-lock[.]yaml
# Artifacts from release.sh
index-local.html
sha256sums[.]asc
asn1js[.]zip
# Artifacts from mirror_to_github.sh
Expand Down
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
ISC License

Copyright (c) 2008-2022 Lapo Luchini <[email protected]>
Copyright (c) 2008-2024 Lapo Luchini <[email protected]>

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
Expand Down
71 changes: 50 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,54 +5,83 @@ asn1js is a JavaScript generic ASN.1 parser/decoder that can decode any valid AS

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).

Usage with `npm` / `yarn`
-------------------------
Usage with `nodejs`
-------------------

This package can be installed with either npm or yarn via the following commands:

```sh
npm install @lapo/asn1js
# or with yarn
# or other tools
pnpm install @lapo/asn1js
yarn add @lapo/asn1js
```

Assuming a standard javascript bundler is setup you can import it like so:
You can import the classes like this:

```js
const ASN1 = require('@lapo/asn1js');
// or with ES modules
import ASN1 from '@lapo/asn1js';
import { ASN1 } from '@lapo/asn1js';
```

A submodule of this package can also be imported:

```js
const Hex = require('@lapo/asn1js/hex');
// or with ES modules
import Hex from '@lapo/asn1js/hex';
import { Hex } from '@lapo/asn1js/hex.js';
```

Usage with RequireJS
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`):

```js
const
{ ASN1 } = require('@lapo/asn1js'),
{ Hex } = require('@lapo/asn1js/hex.js');
console.log(ASN1.decode(Hex.decode('06032B6570')).content());
```

On older NodeJS you instead need to use async `import`:

```js
async function main() {
const
{ ASN1 } = await import('@lapo/asn1js'),
{ Hex } = await import('@lapo/asn1js/hex.js');
console.log(ASN1.decode(Hex.decode('06032B6570')).content());
}
main();
```

Usage on the web
--------------------

Can be [tested on JSFiddle](https://jsfiddle.net/lapo/tmdq35ug/).
Can be [tested on JSFiddle](https://jsfiddle.net/lapo/y6t2wo7q/).

```html
<script type="text/javascript" src="https://unpkg.com/requirejs/require.js"></script>
<script>
require([
'https://unpkg.com/@lapo/asn1js/asn1.js',
'https://unpkg.com/@lapo/asn1js/hex.js'
], function(ASN1, Hex) {
document.body.innerText = ASN1.decode(Hex.decode('06032B6570')).content();
});
import { ASN1 } from 'https://unpkg.com/@lapo/[email protected]/asn1.js';
import { Hex } from 'https://unpkg.com/@lapo/[email protected]/hex.js';
document.body.innerText = ASN1.decode(Hex.decode('06032B6570')).content();
</script>
```

Local usage
--------------------

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. ([known bug](https://github.com/lapo-luchini/asn1js/issues/89): dark mode is currently broken in this mode)

Usage from CLI
--------------------

You can dump an ASN.1 structure from the command line using the following command (no need to even install it):

```sh
npx @lapo/asn1js ed25519.cer
```

ISC license
-----------

ASN.1 JavaScript decoder Copyright (c) 2008-2023 Lapo Luchini <[email protected]>
ASN.1 JavaScript decoder Copyright (c) 2008-2024 Lapo Luchini <[email protected]>

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

Expand All @@ -66,7 +95,7 @@ credits
- extended tag support added by [Péter Budai](https://www.peterbudai.eu/)
- patches by [Gergely Nagy](https://github.com/ngg)
- Relative OID support added by [Mistial Developer](https://github.com/mistial-dev)
- dark mode support added by [Oliver Burgmaier](https://github.com/olibu/)
- dark mode and other UI improvements by [Oliver Burgmaier](https://github.com/olibu/)
- patches by [Nicolai Søborg](https://github.com/NicolaiSoeborg)

links
Expand Down
81 changes: 47 additions & 34 deletions asn1.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
// ASN.1 JavaScript decoder
// Copyright (c) 2008-2023 Lapo Luchini <[email protected]>
// Copyright (c) 2008-2024 Lapo Luchini <[email protected]>

// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
Expand All @@ -13,15 +13,10 @@
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

(typeof define != 'undefined' ? define : function (factory) { 'use strict';
if (typeof module == 'object') module.exports = factory(function (name) { return require(name); });
else window.asn1 = factory(function (name) { return window[name.substring(2)]; });
})(function (require) {
'use strict';
import { Int10 } from './int10.js';
import { oids } from './oids.js';

const
Int10 = require('./int10'),
oids = require('./oids'),
ellipsis = '\u2026',
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)?)?$/,
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)?)?$/,
Expand All @@ -43,7 +38,7 @@ const
['', ''],
['OUou', 'ŐŰőű'], // Double Acute
['AEIUaeiu', 'ĄĘĮŲąęįų'], // Ogonek
['CDELNRSTZcdelnrstz', 'ČĎĚĽŇŘŠŤŽčďěľňřšťž'] // Caron
['CDELNRSTZcdelnrstz', 'ČĎĚĽŇŘŠŤŽčďěľňřšťž'], // Caron
];

function stringCut(str, len) {
Expand Down Expand Up @@ -77,17 +72,21 @@ class Stream {
if (pos === undefined)
pos = this.pos++;
if (pos >= this.enc.length)
throw 'Requesting byte offset ' + pos + ' on a stream of length ' + this.enc.length;
throw new Error('Requesting byte offset ' + pos + ' on a stream of length ' + this.enc.length);
return (typeof this.enc == 'string') ? this.enc.charCodeAt(pos) : this.enc[pos];
}
hexByte(b) {
return hexDigits.charAt((b >> 4) & 0xF) + hexDigits.charAt(b & 0xF);
}
hexDump(start, end, raw) {
/** Hexadecimal dump.
* @param type 'raw', 'byte' or 'dump' */
hexDump(start, end, type = 'dump') {
let s = '';
for (let i = start; i < end; ++i) {
if (type == 'byte' && i > start)
s += ' ';
s += this.hexByte(this.get(i));
if (raw !== true)
if (type == 'dump')
switch (i & 0xF) {
case 0x7: s += ' '; break;
case 0xF: s += '\n'; break;
Expand Down Expand Up @@ -195,7 +194,7 @@ class Stream {
let s = this.parseStringISO(start, end).str,
m = (shortYear ? reTimeS : reTimeL).exec(s);
if (!m)
return 'Unrecognized time: ' + s;
throw new Error('Unrecognized time: ' + s);
if (shortYear) {
// to avoid querying the timer, use the fixed range [1970, 2069]
// it will conform with ITU X.400 [-10, +40] sliding window until 2030
Expand Down Expand Up @@ -250,7 +249,7 @@ class Stream {
parseBitString(start, end, maxLength) {
let unusedBits = this.get(start);
if (unusedBits > 7)
throw 'Invalid BitString with unusedBits=' + unusedBits;
throw new Error('Invalid BitString with unusedBits=' + unusedBits);
let lenBit = ((end - start - 1) << 3) - unusedBits,
s = '';
for (let i = start + 1; i < end; ++i) {
Expand Down Expand Up @@ -371,9 +370,9 @@ class ASN1Tag {
}
}

class ASN1 {
export class ASN1 {
constructor(stream, header, length, tag, tagLen, sub) {
if (!(tag instanceof ASN1Tag)) throw 'Invalid tag value.';
if (!(tag instanceof ASN1Tag)) throw new Error('Invalid tag value.');
this.stream = stream;
this.header = header;
this.length = length;
Expand Down Expand Up @@ -490,7 +489,16 @@ class ASN1 {
}
toPrettyString(indent) {
if (indent === undefined) indent = '';
let s = indent + this.typeName() + ' @' + this.stream.pos;
let s = indent;
if (this.def) {
if (this.def.id)
s += this.def.id + ' ';
if (this.def.name && this.def.name != this.typeName().replace(/_/g, ' '))
s+= this.def.name + ' ';
if (this.def.mismatch)
s += '[?] ';
}
s += this.typeName() + ' @' + this.stream.pos;
if (this.length >= 0)
s += '+';
s += this.length;
Expand Down Expand Up @@ -522,9 +530,12 @@ class ASN1 {
posLen() {
return this.stream.pos + this.tagLen;
}
toHexString() {
return this.stream.hexDump(this.posStart(), this.posEnd(), true);
/** Hexadecimal dump of the node.
* @param type 'raw', 'byte' or 'dump' */
toHexString(type = 'raw') {
return this.stream.hexDump(this.posStart(), this.posEnd(), type);
}
/** Base64 dump of the node. */
toB64String() {
return this.stream.b64Dump(this.posStart(), this.posEnd());
}
Expand All @@ -536,15 +547,15 @@ class ASN1 {
if (len === 0) // long form with length 0 is a special case
return null; // undefined length
if (len > 6) // no reason to use Int10, as it would be a huge buffer anyways
throw 'Length over 48 bits not supported at position ' + (stream.pos - 1);
throw new Error('Length over 48 bits not supported at position ' + (stream.pos - 1));
buf = 0;
for (let i = 0; i < len; ++i)
buf = (buf * 256) + stream.get();
return buf;
}
static decode(stream, offset, type = ASN1) {
if (!(type == ASN1 || type.prototype instanceof ASN1))
throw 'Must pass a class that extends ASN1';
throw new Error('Must pass a class that extends ASN1');
if (!(stream instanceof Stream))
stream = new Stream(stream, offset || 0);
let streamStart = new Stream(stream),
Expand All @@ -560,11 +571,11 @@ class ASN1 {
// definite length
let end = start + len;
if (end > stream.enc.length)
throw 'Container at offset ' + start + ' has a length of ' + len + ', which is past the end of the stream';
throw new Error('Container at offset ' + start + ' has a length of ' + len + ', which is past the end of the stream');
while (stream.pos < end)
sub[sub.length] = type.decode(stream);
if (stream.pos != end)
throw 'Content size is not correct for container at offset ' + start;
throw new Error('Content size is not correct for container at offset ' + start);
} else {
// undefined length
try {
Expand All @@ -576,7 +587,7 @@ class ASN1 {
}
len = start - stream.pos; // undefined lengths are represented as negative values
} catch (e) {
throw 'Exception while decoding undefined length content at offset ' + start + ': ' + e;
throw new Error('Exception while decoding undefined length content at offset ' + start + ': ' + e);
}
}
};
Expand All @@ -588,11 +599,17 @@ class ASN1 {
try {
if (tag.tagNumber == 0x03)
if (stream.get() != 0)
throw 'BIT STRINGs with unused bits cannot encapsulate.';
throw new Error('BIT STRINGs with unused bits cannot encapsulate.');
getSub();
for (let i = 0; i < sub.length; ++i)
if (sub[i].tag.isEOC())
throw 'EOC is not supposed to be actual content.';
for (let s of sub) {
if (s.tag.isEOC())
throw new Error('EOC is not supposed to be actual content.');
try {
s.content();
} catch (e) {
throw new Error('Unable to parse content: ' + e);
}
}
} catch (e) {
// but silently ignore when they don't
sub = null;
Expand All @@ -601,14 +618,10 @@ class ASN1 {
}
if (sub === null) {
if (len === null)
throw "We can't skip over an invalid tag with undefined length at offset " + start;
throw new Error("We can't skip over an invalid tag with undefined length at offset " + start);
stream.pos = start + Math.abs(len);
}
return new type(streamStart, header, len, tag, tagLen, sub);
}

}

return ASN1;

});
Loading

0 comments on commit 240f82c

Please sign in to comment.