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