diff --git a/bin/rasha.js b/bin/rasha.js index 9aea770..a561539 100755 --- a/bin/rasha.js +++ b/bin/rasha.js @@ -1,7 +1,6 @@ #!/usr/bin/env node 'use strict'; var fs = require('fs'); - var infile = process.argv[2]; var pem = fs.readFileSync(infile, 'ascii'); @@ -14,60 +13,54 @@ var b64 = pem.split(/\n/).filter(function (line) { }).join(''); var buf = Buffer.from(b64, 'base64'); +var ELOOP = "uASN1.js Error: iterated over 100+ elements (probably a malformed file)"; +var EDEEP = "uASN1.js Error: element nested 100+ layers deep (probably a malformed file)"; +var ASN1 = require('../lib/uasn1.js'); +/* function ASN1(buf, depth) { - console.log(); - if (!depth) { depth = 0; } - if (depth >= 15) { - throw new Error("We got stuck in a loop (or this is actually more than 15 layers deep, which we never expected)"); + if (depth >= 100) { + throw new Error(EDEEP); } + + // start after type (0) and lengthSize (1) + var index = 2; var asn1 = { - index: 2 // start after type (0) and lengthSize (1) - , type: buf[0] - , length: buf[1] + type: buf[0] , lengthSize: 0 - , value: null - , children: [] + , length: buf[1] }; - console.log(depth, buf.byteLength, buf); var child; var i = 0; if (0x80 & asn1.length) { asn1.lengthSize = 0x7f & asn1.length; // I think that buf->hex->int solves the problem of Endianness... not sure - console.log('lenlen:', buf.slice(asn1.index, asn1.index + asn1.lengthSize).toString('hex')); - asn1.length = parseInt(buf.slice(asn1.index, asn1.index + asn1.lengthSize).toString('hex'), 16); + asn1.length = parseInt(buf.slice(index, index + asn1.lengthSize).toString('hex'), 16); // add back the original byte indicating lengthSize - //asn1.lengthSize += 1; - //asn1.index += asn1.lengthSize; - asn1.index += 1; + index += 1; } - console.log('asn1', 'type', Buffer.from([asn1.type]).toString('hex') - , 'ls', Buffer.from([asn1.lengthSize]).toString('hex'), 'len', asn1.length - , 'ch', asn1.children.length, 'vlen', asn1.value && asn1.value.length || null); - // this is a primitive value type if (asn1.type <= 0x06) { i += 1; - asn1.value = buf.slice(asn1.index, asn1.index + asn1.length); + asn1.value = buf.slice(index, index + asn1.length); return asn1; } - while (i < 12 && asn1.index < buf.byteLength) { - var childbuf = buf.slice(asn1.index); - child = ASN1(childbuf, depth += 1); - console.log('child', 'type', Buffer.from([child.type]).toString('hex') - , 'ls', Buffer.from([child.lengthSize]).toString('hex'), 'len', child.length - , 'ch', child.children.length, 'vlen', child.value && child.value.length || null); - asn1.index += 2 /*child.type.length*/ + child.lengthSize + child.length; + asn1.children = []; + while (i < 100 && index < buf.byteLength) { + child = ASN1(buf.slice(index), (depth || 0) + 1); + index += (2 + child.lengthSize + child.length); asn1.children.push(child); } - - if (i >= 12) { - throw new Error("malformed ASN1: got stuck in a read loop (or there were actually 100+ elements in a sequence, which we never expected)"); - } + if (i >= 100) { throw new Error(ELOOP); } return asn1; } +*/ -console.log(ASN1(buf)); +var asn1 = ASN1.parse(buf); +console.log(asn1); +console.log(asn1.type, asn1.length); +asn1.children.forEach(function (a, i) { + console.log(i, 'ch', Buffer.from([a.type]).toString('hex'), a.length); +}); diff --git a/lib/uasn1.js b/lib/uasn1.js new file mode 100644 index 0000000..f5ae5fc --- /dev/null +++ b/lib/uasn1.js @@ -0,0 +1,48 @@ +'use strict'; + +var ELOOP = "uASN1.js Error: iterated over 100+ elements (probably a malformed file)"; +var EDEEP = "uASN1.js Error: element nested 100+ layers deep (probably a malformed file)"; +// Container Types are Sequence 0x30, Octect String 0x04, Array? (0xA0, 0xA1) +// Value Types are Integer 0x02, Bit String 0x03, Null 0x05, Object ID 0x06, +// Sometimes Bit String is used as a container (RSA Pub Spki) +var VTYPES = [ 0x02, 0x03, 0x05, 0x06 ]; + +var ASN1 = module.exports = function ASN1() { +}; + +ASN1.parse = function parseAsn1(buf, depth) { + if (depth >= 100) { throw new Error(EDEEP); } + + var index = 2; // we know, at minimum, data starts after type (0) and lengthSize (1) + var asn1 = { type: buf[0], lengthSize: 0, length: buf[1] }; + var child; + var iters = 0; + + // Determine how many bytes the length uses, and what it is + if (0x80 & asn1.length) { + asn1.lengthSize = 0x7f & asn1.length; + // I think that buf->hex->int solves the problem of Endianness... not sure + asn1.length = parseInt(buf.slice(index, index + asn1.lengthSize).toString('hex'), 16); + //index += 1; // add back the original byte indicating lengthSize + index += asn1.lengthSize; + } + + // this is a primitive value type + if (-1 !== VTYPES.indexOf(asn1.type)) { + asn1.value = buf.slice(index, index + asn1.length); + return asn1; + } + + asn1.children = []; + while (iters < 100 && index < buf.byteLength) { + iters += 1; + child = ASN1.parse(buf.slice(index), (depth || 0) + 1); + index += (2 + child.lengthSize + child.length); + asn1.children.push(child); + } + if (iters >= 100) { throw new Error(ELOOP); } + + return asn1; +}; + +module.exports = ASN1;