JavaScript generic ASN.1 parser (mirror)
at github-56 4.1 kB view raw
1#!/usr/bin/env node 2'use strict'; 3 4const 5 fs = require('fs'), 6 Base64 = require('./base64.js'), 7 ASN1 = require('./asn1.js'), 8 rfc = require('./rfcasn1.json'), 9 colYellow = "\x1b[33m", 10 colBlue = "\x1b[34m", 11 colReset = "\x1b[0m"; 12 13function searchType(name) { 14 for (const r of Object.values(rfc)) 15 if (name in r.types) { 16 // console.log(name + ' found in ' + r.name); 17 return r.types[name]; 18 } 19 throw 'Type not found: ' + name; 20} 21 22function translate(def, tn) { 23 const id = def?.id; 24 if (def?.type == 'tag' && !def.explicit) 25 // def.type = def.content[0].type; 26 def = def.content[0].type; 27 while (def?.type == 'defined' || def?.type?.type == 'defined') { 28 const name = def?.type?.type ? def.type.name : def.name; 29 def = Object.assign({}, def); 30 def.type = searchType(name).type; 31 } 32 if (def?.type?.name == 'CHOICE') { 33 for (let c of def.type.content) { 34 c = translate(c); 35 if (tn == c.type.name || tn == c.name) { 36 def = Object.assign({}, def); 37 def.type = c.type.name ? c.type : c; 38 break; 39 } 40 } 41 } 42 if (id) 43 def = Object.assign({}, def, { id }); 44 return def ?? { type: {} }; 45} 46 47function firstUpper(s) { 48 return s[0].toUpperCase() + s.slice(1); 49} 50 51function print(value, def, stats, indent) { 52 if (indent === undefined) indent = ''; 53 stats ??= {}; 54 stats.total ??= 0; 55 stats.recognized ??= 0; 56 stats.defs ??= {}; 57 let tn = value.typeName(); 58 def = translate(def, tn); 59 tn = tn.replaceAll('_', ' '); 60 if (stats) ++stats.total; 61 let name = ''; 62 if (def?.type) { 63 if (def.id) name += colBlue + def.id + colReset; 64 if (typeof def.type == 'object' && def.name) name = (name ? name + ' ' : '') + def.name; 65 if (stats && name != '') ++stats.recognized; 66 if (name) name += ' '; 67 } 68 let s = indent + name + colYellow + value.typeName() + colReset + " @" + value.stream.pos; 69 if (value.length >= 0) 70 s += "+"; 71 s += value.length; 72 if (value.tag.tagConstructed) 73 s += " (constructed)"; 74 else if ((value.tag.isUniversal() && ((value.tag.tagNumber == 0x03) || (value.tag.tagNumber == 0x04))) && (value.sub !== null)) 75 s += " (encapsulates)"; 76 let content = value.content(); 77 if (content) 78 s += ": " + content.replace(/\n/g, '|'); 79 s += "\n"; 80 if (value.sub !== null) { 81 indent += ' '; 82 if (def?.type?.type) 83 def = def.type; 84 let j = def?.content ? 0 : -1; 85 for (const subval of value.sub) { 86 let type; 87 if (j >= 0) { 88 if (def.typeOf) 89 type = def.content[0]; 90 else { 91 let tn = subval.typeName(); //.replaceAll('_', ' '); 92 do { 93 type = def.content[j++]; 94 // type = translate(type, tn); 95 if (type?.type?.type) 96 type = type.type; 97 } while (type && ('optional' in type || 'default' in type) && type.name != 'ANY' && type.name != tn); 98 if (type?.type == 'defined') 99 stats.defs[type.id] = subval.content().split(/\n/); 100 else if (type?.definedBy && stats.defs?.[type.definedBy]?.[1]) // hope current OIDs contain the type name (will need to parse from RFC itself) 101 type = searchType(firstUpper(stats.defs[type.definedBy][1])); 102 } 103 } 104 s += print(subval, type, stats, indent); 105 } 106 } 107 return s; 108} 109 110let content = fs.readFileSync(process.argv[3]); 111try { // try PEM first 112 content = Base64.unarmor(content); 113} catch (e) { // try DER/BER then 114} 115let result = ASN1.decode(content); 116content = null; 117let stats = {}; 118console.log(print(result, searchType(process.argv[2]), stats)); 119console.log('Stats:', (stats.recognized * 100 / stats.total).toFixed(2) + '%'); 120// console.log('Defs:', stats.defs);