# @root/asn1 Built by [The Root Company](https://therootcompany.com) for [Greenlock](https://greenlock.domains) 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*** ``` \* INTEGERS are always BigInt-encoded (a leading '00' for positive numbers with a 1 in the most-significant-bit position) \*\*BIT STRINGS have a leading "bit mask" which, for all practical purposes, is actually _always_ '00' \*\*\* See 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: ``, 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: }, { type: 4, lengthSize: 0, length: 32, value: , 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 ``` ```html ``` ```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: - Patreon: 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 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)