asn1.js/README.md

413 lines
9.2 KiB
Markdown
Raw Normal View History

# @root/asn1
2019-10-12 23:54:53 +00:00
Built by [The Root Company](https://therootcompany.com)
for [Greenlock](https://greenlock.domains)
2019-10-13 06:29:07 +00:00
and [Keypairs.js](https://git.rootprojects.org/root/keypairs.js)
Lightweight, Zero-Dependency ASN.1 encoder and decoder for Node.js and Browsers,
in less than 300 lines of vanilla JavaScript
| 1.6k gzipped
| 4.2k minified
| 8.4k pretty
|
- [x] Zero External Dependencies
- [x] Universal Support
- [x] Node.js
- [x] Browsers
- [x] Vanilla JS
This ASN.1 codec is built for simplicity. It encodes into DER format
and decodes into a simple, classless Array of Arrays and values.
Most people don't actually want to work with ANS.1 directly,
but rather intend to work with pre-defined x509 schemas.
If you're **most people**, you're actually looking for one or more of these:
- [pem.js](https://git.rootprojects.org/root/pem.js)
- [x509.js](https://git.rootprojects.org/root/x509.js)
- [csr.js](https://git.rootprojects.org/root/csr.js)
- [keypairs.js](https://git.rootprojects.org/root/keypairs.js)
- [encoding.js](https://git.rootprojects.org/root/encoding.js)
Want to [contribute](#contributions)?
Need [commercial support](#commercial-support)?
# Usage
ASN.1 DER consists values which have
- a type (2-bit class, 6-bit tag)
- a coded length
- zero or more values
Common types include:
```txt
0x30 SEQUENCE
0x02 INTEGER*
0x03 BIT STRING**
0x04 OCTET STRING
0x05 NULL
0x06 OBJECT IDENTIFIER
0x0C UTF8String
0x16 IA5String (ASCII)
0x17 UTCTime
0x31 SET
0xA0 context-specific***
0xA3 context-specific***
```
<small>\* INTEGERS are always BigInt-encoded (a leading '00' for positive numbers with a 1 in the most-significant-bit position)</small>
<small>\*\*BIT STRINGS have a leading "bit mask" which, for all practical purposes, is actually _always_ '00'</small>
<small>\*\*\* See <https://stackoverflow.com/a/15071901/151312></small>
The core value in this library is that it:
- correctly sums the byte length of children elements
- correctly encodes BigInts
## Parser Usage
There are three options:
- `der` (required) - the input bytes as a buffer
- `json` (default) - returns hex strings for values, rather than buffers
- `verbose` - returns a more human-friendly object that is useful for debugging
```js
ASN1.parse({ der: `<Buffer>`, json: true, verbose: true });
```
Default (hex) output:
```js
[
'30',
[
['02', '01'],
['04', '2c8996...'],
['a0', [['06', '2a8648...']]],
['a1', [['03', '04bdd8...']]]
]
];
```
Verbose output:
```js
{ type: 48,
lengthSize: 0,
length: 119,
children:
[ { type: 2, lengthSize: 0, length: 1, value: <Buffer 01> },
{ type: 4,
lengthSize: 0,
length: 32,
value:
<Buffer 2c 89 96 ...>,
children: [] },
{ type: 160, lengthSize: 0, length: 10, children: [Array] },
{ type: 161, lengthSize: 0, length: 68, children: [Array] } ] }
```
## Packer Usage
You can use either of two syntaxes. One is much easier to read than the other.
Ironically, hex strings are used in place of buffers for efficiency.
```js
ASN1.Any(hexType, hexBytes1, hexBytes2, ...);
ASN1.UInt(hexBigInt);
ASN1.BitStr(hexBitStream);
```
In practice, you'll be cascading the objects into a final hex string:
```
// result is a hex-encoded DER
var der = hexToBuf(
ASN1.Any('30' // Sequence
, ASN1.UInt('01') // Integer (Version 1)
, ASN1.Any('04', '07CAD7...') // Octet String
, ASN1.Any('A0', '06082A...') // [0] Object ID (context-specific)
, ASN1.Any('A1', // [1] (context-specific value)
ASN1.BitStr('04BDD8...')
)
)
);
```
Alternatively you can pack either the sparse array or verbose object, using hex strings or buffers:
- `json` when set to true will return a hex-encoded DER rather than a DER buffer
```js
var buf = Uint8Array.from([0x01]);
ASN1.pack(
[
'30',
[
['02', buf],
['04', '07CAD7...'],
['A0', '06082A...'],
['A1', ['03', '04BDD8...']]
]
],
{ json: false }
);
```
```js
var buf = Uint8Array.from([0x01]);
ASN1.pack(
{
type: 48,
children: [
{ type: 2, value: '01' },
{ type: 4, value: '2c 89 96 ...', children: [] },
{ type: 160, children: [...] },
{ type: 161, children: [...] }
]
},
{ json: false }
);
```
# Install
This package contains both node-specific and browser-specific code,
and the `package.json#browser` field ensures that your package manager
will automatically choose the correct code for your environment.
## Node (and Webpack)
```js
npm install -g @root/asn1
```
```js
var asn1 = require('@root/asn1');
```
```js
// just the packer
var asn1 = require('@root/asn1/packer');
// just the parser
var asn1 = require('@root/asn1/parser');
```
## Browsers (Vanilla JS)
```html
<script src="https://unpkg.com/@root/asn1/dist/asn1.all.js"></script>
```
```html
<script src="https://unpkg.com/@root/asn1/dist/asn1.all.min.js"></script>
```
```js
var ASN1 = window.ASN1;
```
# Examples
## Decoding DER to JSON-ASN.1
```js
var PEM = require('@root/pem/packer');
var Enc = require('@root/encoding');
var ASN1 = require('@root/asn1/parser');
```
```js
var pem = [
'-----BEGIN EC PRIVATE KEY-----',
'MHcCAQEEICyJlsaqkx2z9yx0H6rHA0lM3/7jXjxqn/VOhExHDuR6oAoGCCqGSM49',
'AwEHoUQDQgAEvdjQ3T6VBX82LIKDzepYgRsz3HgRwp83yPuonu6vqoshSQRe0Aye',
'mmdXUDX2wTZsmFSjhY9uroRiBbGZrigbKA==',
'-----END EC PRIVATE KEY-----'
].join('\n');
```
```js
var der = PEM.parseBlock(pem).bytes;
var asn1 = ASN1.parse({ der: der, json: true, verbose: false });
```
```json
[
"30",
[
["02", "01"],
[
"04",
"2c8996c6aa931db3f72c741faac703494cdffee35e3c6a9ff54e844c470ee47a"
],
["a0", [["06", "2a8648ce3d030107"]]],
[
"a1",
[
[
"03",
"04bdd8d0dd3e95057f362c8283cdea58811b33dc7811c29f37c8fba89eeeafaa8b2149045ed00c9e9a67575035f6c1366c9854a3858f6eae846205b199ae281b28"
]
]
]
]
]
```
```json
{
"type": 48,
"lengthSize": 0,
"length": 119,
"children": [
{ "type": 2, "lengthSize": 0, "length": 1, "value": "01" },
{
"type": 4,
"lengthSize": 0,
"length": 32,
"value": "2c8996c6aa931db3f72c741faac703494cdffee35e3c6a9ff54e844c470ee47a",
"children": []
},
{
"type": 160,
"lengthSize": 0,
"length": 10,
"children": [
{
"type": 6,
"lengthSize": 0,
"length": 8,
"value": "2a8648ce3d030107"
}
]
},
{
"type": 161,
"lengthSize": 0,
"length": 68,
"children": [
{
"type": 3,
"lengthSize": 0,
"length": 66,
"value": "04bdd8d0dd3e95057f362c8283cdea58811b33dc7811c29f37c8fba89eeeafaa8b2149045ed00c9e9a67575035f6c1366c9854a3858f6eae846205b199ae281b28",
"children": []
}
]
}
]
}
```
## Encoding ASN.1 to DER
Here's an example of an SEC1-encoded EC P-256 Public/Private Keypair:
```js
var ASN1 = require('@root/asn1/packer');
var Enc = require('@root/encoding');
var PEM = require('@root/pem/packer');
```
```js
// 1.2.840.10045.3.1.7
// prime256v1 (ANSI X9.62 named elliptic curve)
var OBJ_ID_EC_256 = '06 08 2A8648CE3D030107';
```
```js
var jwk = {
crv: 'P-256',
d: 'LImWxqqTHbP3LHQfqscDSUzf_uNePGqf9U6ETEcO5Ho',
kty: 'EC',
x: 'vdjQ3T6VBX82LIKDzepYgRsz3HgRwp83yPuonu6vqos',
y: 'IUkEXtAMnppnV1A19sE2bJhUo4WPbq6EYgWxma4oGyg',
kid: 'MnfJYyS9W5gUjrJLdn8ePMzik8ZJz2qc-VZmKOs_oCw'
};
var d = Enc.base64ToHex(jwk.d);
var x = Enc.base64ToHex(jwk.x);
var y = Enc.base64ToHex(jwk.y);
```
```
var der = Enc.hexToBuf(
ASN1.Any('30' // Sequence
, ASN1.UInt('01') // Integer (Version 1)
, ASN1.Any('04', d) // Octet String
, ASN1.Any('A0', OBJ_ID_EC_256) // [0] Object ID
, ASN1.Any('A1', // [1] Embedded EC/ASN1 public key
ASN1.BitStr('04' + x + y)
)
)
);
var pem = PEM.packBlock({
type: 'EC PRIVATE KEY',
bytes: der
});
```
# Disabiguation
`ASN1.Any(typ, hexVal, ...)`
There was once an actual ASN.1 type with the literal name 'Any'.
It was deprecated in 1994 and the `Any` in this API simply means "give any value"
# Contributions
Did this project save you some time? Maybe make your day? Even save the day?
Please say "thanks" via Paypal or Patreon:
- Paypal: [\$5](https://paypal.me/rootprojects/5) | [\$10](https://paypal.me/rootprojects/10) | Any amount: <paypal@therootcompany.com>
- Patreon: <https://patreon.com/rootprojects>
Where does your contribution go?
[Root](https://therootcompany.com) is a collection of experts
who trust each other and enjoy working together on deep-tech,
Indie Web projects.
Our goal is to operate as a sustainable community.
Your contributions - both in code and _especially_ monetarily -
help to not just this project, but also our broader work
of [projects](https://rootprojects.org) that fuel the **Indie Web**.
Also, we chat on [Keybase](https://keybase.io)
in [#rootprojects](https://keybase.io/team/rootprojects)
# Commercial Support
Do you need...
- more features?
- bugfixes, on _your_ timeline?
- custom code, built by experts?
- commercial support and licensing?
Contact <aj@therootcompany.com> for support options.
# Legal
Copyright [AJ ONeal](https://coolaj86.com),
[Root](https://therootcompany.com) 2018-2019
MPL-2.0 |
[Terms of Use](https://therootcompany.com/legal/#terms) |
[Privacy Policy](https://therootcompany.com/legal/#privacy)