/ dumpASN1.js
dumpASN1.js
1 #!/usr/bin/env node 2 3 // usage: 4 // ./dumpASN1.js filename 5 // ./dumpASN1.js data:base64,MDMCAQFjLgQACgEACgEAAgEAAgEAAQEAoA+jDQQFTnRWZXIEBAEAAAAwCgQITmV0bG9nb24=== 6 // cat file.der | ./dumpASN1.js - 7 8 import * as fs from 'node:fs'; 9 import { text as streamAsString } from 'node:stream/consumers'; 10 import { Base64 } from './base64.js'; 11 import { ASN1 } from './asn1.js'; 12 import { Defs } from './defs.js'; 13 14 const 15 colYellow = '\x1b[33m', 16 colBlue = '\x1b[34m', 17 colReset = '\x1b[0m', 18 reDataURI = /^data:(?:[a-z-]+[/][a-z.+-]+;)?base64,([A-Za-z0-9+/=\s]+)$/; 19 20 function print(value, indent) { 21 if (indent === undefined) indent = ''; 22 const def = value.def; 23 let name = ''; 24 if (def?.type) { 25 if (def.id) name += colBlue + def.id + colReset; 26 if (typeof def.type == 'object' && def.name) name = (name ? name + ' ' : '') + def.name; 27 if (def.mismatch) name = (name ? name + ' ' : '') + '[?]'; 28 if (name) name += ' '; 29 } 30 let s = indent + name + colYellow + value.typeName() + colReset + ' @' + value.stream.pos; 31 if (value.length >= 0) 32 s += '+'; 33 s += value.length; 34 if (value.tag.tagConstructed) 35 s += ' (constructed)'; 36 else if ((value.tag.isUniversal() && ((value.tag.tagNumber == 0x03) || (value.tag.tagNumber == 0x04))) && (value.sub !== null)) 37 s += ' (encapsulates)'; 38 let content = value.content(); 39 if (content) 40 s += ': ' + content.replace(/\n/g, '|'); 41 s += '\n'; 42 if (value.sub !== null) { 43 indent += ' '; 44 for (const subval of value.sub) 45 s += print(subval, indent); 46 } 47 return s; 48 } 49 50 const filename = process.argv[2]; 51 let content; 52 const match = reDataURI.exec(filename); 53 if (match) 54 content = Buffer.from(match[1]); 55 else if (filename == '-') // stdin 56 content = await streamAsString(process.stdin); 57 else 58 content = fs.readFileSync(filename); 59 try { // try PEM first 60 content = Base64.unarmor(content); 61 } catch (ignore) { // try DER/BER then 62 } 63 let result = ASN1.decode(content); 64 content = null; 65 const t0 = performance.now(); 66 if (process.argv.length == 5) { 67 Defs.match(result, Defs.moduleAndType(Defs.RFC[process.argv[3]], process.argv[4])); 68 } else { 69 const types = Defs.commonTypes 70 .map(type => { 71 const stats = Defs.match(result, type); 72 return { type, match: stats.recognized / stats.total }; 73 }) 74 .sort((a, b) => b.match - a.match); 75 const t1 = performance.now(); 76 console.log('Parsed in ' + (t1 - t0).toFixed(2) + ' ms; possible types:'); 77 for (const t of types) 78 console.log((t.match * 100).toFixed(2).padStart(6) + '% ' + t.type.description); 79 Defs.match(result, types[0].type); 80 // const stats = Defs.match(result, types[0].type); 81 // console.log('Stats:', stats); 82 console.log('Parsed as:', result.def); 83 // const type = searchType(process.argv[2]); 84 // const stats = applyDef(result, type); 85 } 86 console.log(print(result)); 87 // console.log('Stats:', (stats.recognized * 100 / stats.total).toFixed(2) + '%'); 88 // // print(result, searchType(process.argv[2]), stats); 89 // // console.log('Defs:', stats.defs);