Color output for derdump (after "pip install colored")
[hexio] / derdump
1 #!/usr/bin/env python
2
3 import sys
4
5 import optparse
6
7 opts = optparse.OptionParser ()
8 opts.add_option ('-c', '--colour', '--color',
9                 action='store_true', default=False, dest='colour',
10                 help='Print colourful output (loads Python module "colored", page with "less -R")')
11 (options,args) = opts.parse_args ()
12
13 if len (args) != 1:
14         print 'Usage:  ' + attr ('bold') + sys.argv [0] + '[--colour] file.der' + attr (0)
15         print 'Output: ' + nicemeaning ('MEANING') + ': ' + nicetag ('TAG') + ' ' + nicecontlen ('CONTLEN', 3) + ' ' + nicetagofs ('TAGOFS') + ' ' + nicenesting ('NESTING') + ', ' + niceclass ('CLASS') + ', ' + niceprimconstr ('PRIMCONSTR')
16         sys.exit (1)
17
18 if options.colour:
19         from colored import fg, bg, attr
20 else:
21         def attr (_col):
22                 return ''
23         fg = attr
24         bg = attr
25
26
27 class2str = {
28         0: 'Universal',
29         1: 'Application',
30         2: 'Contextual',
31         3: 'Private'
32 }
33
34 pc2str = {
35         0: 'Primitive',
36         1: 'Constructed'
37 }
38
39 universal2str = {
40         0: 'End-Of-Content',
41         1: 'BOOLEAN',
42         2: 'INTEGER',
43         3: 'BITSTRING',
44         4: 'OCTETSTRING',
45         5: 'NULL',
46         6: 'OID',
47         7: 'Object-Descriptor',
48         8: 'EXTERNAL',
49         9: 'REAL',
50         10: 'ENUMERATED',
51         11: 'EMBEDDED PDV',
52         12: 'UTF8String',
53         13: 'RELATIVE-OID',
54         14: '*****',
55         15: '*****',
56         16: 'SEQUENCE (OF)',
57         17: 'SET (OF)',
58         18: 'NumericString',
59         19: 'PrintableString',
60         20: 'T61String',
61         21: 'VideotexString',
62         22: 'IA5String',
63         23: 'UTCTime',
64         24: 'GeneralizedTime',
65         25: 'GraphicString',
66         26: 'VisibleString',
67         27: 'GeneralString',
68         28: 'UniversalString',
69         29: 'CHARACTER STRING',
70         30: 'BMPString',
71         31: '*****'
72 }
73
74
75 def niceerror (s):
76         return bg ('yellow') + fg ('red') + s + attr (0)
77
78 def nicemeaning (s):
79         return attr ('bold') + s + attr (0)
80
81 def nicetag (ds):
82         if type (ds) == int:
83                 ds = 'tag 0x%02x' % ds
84         return fg ('green') + ds + attr (0)
85
86 def nicecontlen (ds, depth):
87         if type (ds) == int:
88                 ds = str (ds)
89         return fg ('magenta') + '#' * depth + ds + attr (0)
90
91 def nicetagofs (ds):
92         if type (ds) == int:
93                 ds = str (ds)
94         return fg ('cyan') + '@' + ds + attr (0)
95
96 def nicenesting (ad):
97         if type (ad) == type ([]):
98                 ad = len (ad)
99         return fg ('light_blue') + '^' + str (ad) + attr (0)
100
101 def niceclass (ds):
102         if type (ds) == int:
103                 ds = class2str [ds]
104         return attr ('dim') + ds + attr (0)
105
106 def niceprimconstr (ds):
107         if type (ds) == bool:
108                 ds = int (ds)
109         if type (ds) == int:
110                 ds = pc2str [ds]
111         return attr ('dim') + ds + attr (0)
112
113 der = open (args [0], 'r').read (65537)
114 ofs = 0
115
116 def eof ():
117         global ofs, der
118         return ofs >= len (der)
119
120 def read1 ():
121         global ofs, der
122         if eof ():
123                 print 'ATTEMPTED READ BEYOND EOF (RETURNING 0x00)'
124                 return 0
125         else:
126                 ofs = ofs + 1
127                 return ord (der [ofs-1])
128
129 nesting = []
130
131 while not eof ():
132
133         while nesting != [] and ofs >= nesting [-1]:
134                 if ofs > nesting [-1]:
135                         print 'READ OFFSET %d EXCEEDS ENCAPSULATION %d (RETURNING)' % (ofs, nesting [-1])
136                 ofs = nesting.pop ()
137
138         tag = read1 ()
139         tag_class = (tag & 0xc0) >> 6
140         tag_pc = (tag & 0x20) != 0
141         tag_num = tag & 0x1f
142
143         lenlen = read1 ()
144         if lenlen & 0x80 == 0:
145                 leng = lenlen
146                 lenlen = 1
147         else:
148                 lenlen = lenlen - 0x80 + 1
149                 leng = 0
150                 i = 1
151                 while i < lenlen:
152                         leng <<= 8
153                         leng = leng + read1 ()
154                         i = i + 1
155
156         if tag_class == 0:
157                 meaning = universal2str [tag_num]
158         elif tag_class == 1:
159                 meaning = '[APPLICATION ' + str (tag_num) + ']'
160         elif tag_class == 2:
161                 meaning = '[' + str (tag_num) + ']'
162         else:
163                 meaning = '[PRIVATE ' + str (tag_num) + ']'
164
165         print ('%s%s: %s %s %s %s, %s, %s' % (
166                         '  ' * len (nesting),
167                         nicemeaning (meaning), nicetag (tag),
168                         nicecontlen (leng, lenlen),
169                         nicetagofs (ofs - lenlen - 1),
170                         nicenesting (nesting),
171                         niceclass (tag_class),
172                         niceprimconstr (tag_pc) ) )
173
174         if tag_pc == 0 and leng > 0:
175                 print '  ' * ( len (nesting) + 1 ),
176                 cstr = ''
177                 ival = None
178                 ostr = ''
179                 oval = None
180                 while leng > 0:
181                         ch = read1 ()
182                         print '%02x' % ch,
183                         if 32 <= ch < 127:
184                                 cstr = cstr + chr (ch)
185                         else:
186                                 cstr = cstr + '.'
187                         if ival is None:
188                                 ival = -1 if ch >= 128 else 0
189                         else:
190                                 ival = (ival << 8) | ch
191                         if oval is None:
192                                 ostr = str (ch / 40) + '.' + str (ch % 40)
193                                 oval = 0
194                         else:
195                                 oval = (oval << 7) | (ch & 0x7f)
196                                 if ch & 0x80 == 0:
197                                         ostr = ostr + '.' + str (oval)
198                                         oval = 0
199                         leng = leng - 1
200                 if tag == 0x06:
201                         cstr = attr ('bold') + ostr + attr (0)
202                 elif tag == 0x02:
203                         cstr = attr ('bold') + str (ival) + attr (0)
204                 else:
205                         cstr = '"' + attr ('bold') + cstr + attr (0) + '"'
206                 print '==', cstr
207
208         if tag_pc != 0:
209                 # print 'Now at', ofs, 'adding', leng, 'pushing', ofs + leng
210                 nesting.append (ofs + leng)
211
212 while nesting != []:
213         if ofs != nesting [-1]:
214                 print niceerror ('NESTING NOT ENDED CORRECTLY, OFFSET IS %d INSTEAD OF %d (CONTINUING)' % (ofs, nesting [-1]))
215         nesting.pop ()
216