From 665a2d2f8e47e4f175409ce730e5258791d71324 Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Mon, 17 Dec 2018 21:29:25 -0700 Subject: [PATCH] asn1-packer.js --- lib/asn1-packer.js | 110 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 lib/asn1-packer.js diff --git a/lib/asn1-packer.js b/lib/asn1-packer.js new file mode 100644 index 0000000..55262a1 --- /dev/null +++ b/lib/asn1-packer.js @@ -0,0 +1,110 @@ +'use strict'; + +var ASN1 = module.exports; +var Enc = require('./encoding.js'); + +// +// Packer +// + +// Almost every ASN.1 type that's important for CSR +// can be represented generically with only a few rules. +ASN1.Any = function (/*type, hexstrings...*/) { + var args = Array.prototype.slice.call(arguments); + var typ = args.shift(); + var str = args.join('').replace(/\s+/g, '').toLowerCase(); + var len = (str.length/2); + var lenlen = 0; + var hex = typ; + + // We can't have an odd number of hex chars + if (len !== Math.round(len)) { + throw new Error("invalid hex"); + } + + // The first byte of any ASN.1 sequence is the type (Sequence, Integer, etc) + // The second byte is either the size of the value, or the size of its size + + // 1. If the second byte is < 0x80 (128) it is considered the size + // 2. If it is > 0x80 then it describes the number of bytes of the size + // ex: 0x82 means the next 2 bytes describe the size of the value + // 3. The special case of exactly 0x80 is "indefinite" length (to end-of-file) + + if (len > 127) { + lenlen += 1; + while (len > 255) { + lenlen += 1; + len = len >> 8; + } + } + + if (lenlen) { hex += Enc.numToHex(0x80 + lenlen); } + return hex + Enc.numToHex(str.length/2) + str; +}; + +// The Integer type has some special rules +ASN1.UInt = function UINT() { + var str = Array.prototype.slice.call(arguments).join(''); + var first = parseInt(str.slice(0, 2), 16); + + // If the first byte is 0x80 or greater, the number is considered negative + // Therefore we add a '00' prefix if the 0x80 bit is set + if (0x80 & first) { str = '00' + str; } + + return ASN1.Any('02', str); +}; + +// The Bit String type also has a special rule +ASN1.BitStr = function BITSTR() { + var str = Array.prototype.slice.call(arguments).join(''); + // '00' is a mask of how many bits of the next byte to ignore + return ASN1.Any('03', '00' + str); +}; + +ASN1._pack = function (arr) { + var typ = Enc.numToHex(arr[0]); + var str = ''; + if (Array.isArray(arr[1])) { + arr[1].forEach(function (a) { + str += ASN1._pack(a); + }); + } else if ('string' === typeof arr[1]) { + str = arr[1]; + } else { + throw new Error("unexpected array"); + } + if ('03' === typ) { + return ASN1.BitStr(str); + } else if ('02' === typ) { + return ASN1.UInt(str); + } else { + return ASN1.Any(typ, str); + } +}; +ASN1.pack = function (arr) { + return Enc.hexToBuf(ASN1._pack(arr)); +}; + +Enc.bufToBase64 = function (u8) { + var bin = ''; + u8.forEach(function (i) { + bin += String.fromCharCode(i); + }); + return btoa(bin); +}; + +Enc.hexToBuf = function (hex) { + var arr = []; + hex.match(/.{2}/g).forEach(function (h) { + arr.push(parseInt(h, 16)); + }); + return 'undefined' !== typeof Uint8Array ? new Uint8Array(arr) : arr; +}; + +Enc.numToHex = function (d) { + d = d.toString(16); + if (d.length % 2) { + return '0' + d; + } + return d; +};