88# the file "AUTHORS" for a complete overview.
99
1010from __future__ import absolute_import , division , print_function , unicode_literals
11+
1112from builtins import open , bytes , str
12- import sys
13+ import asn1
1314import base64
1415import binascii
15-
16- import asn1
16+ import io
17+ import sys
18+ import typing
1719import optparse
1820
1921
20- def read_pem (input_file ):
21- """Read PEM formatted input."""
22- data = []
23- state = 0
24- for line in input_file :
25- if state == 0 :
26- if line .startswith ('-----BEGIN' ):
27- state = 1
28- elif state == 1 :
29- if line .startswith ('-----END' ):
30- state = 2
31- else :
32- data .append (line )
33- elif state == 2 :
34- break
35- if state != 2 :
36- raise ValueError ('No PEM encoded input found' )
37- data = '' .join (data )
38- return base64 .b64decode (data )
22+ class PEMBinaryIO (io .RawIOBase ):
23+ """A RawIOBase implementation that reads and writes PEM encoded data."""
24+ def __init__ (self , stream ):
25+ self ._stream = stream
26+ self ._buffer = b''
27+ self ._state = 0
28+
29+ def read (self , size = - 1 ):
30+ while size < 0 or len (self ._buffer ) < size :
31+ line = self ._stream .readline ()
32+ if not line :
33+ break
34+ line = line .strip ()
35+
36+ if self ._state == 0 :
37+ if line .startswith ('-----BEGIN' ):
38+ self ._state = 1
39+ elif self ._state == 1 :
40+ if line .startswith ('-----END' ):
41+ self ._state = 2
42+ else :
43+ self ._buffer += base64 .b64decode (line )
44+
45+ # All data?
46+ if size < 0 :
47+ data , self ._buffer = self ._buffer , b''
48+ return data
49+
50+ # Only a portion of the data
51+ data , self ._buffer = self ._buffer [:size ], self ._buffer [size :]
52+ return data
53+
54+ def write (self , data ):
55+ self ._stream .write (base64 .b64encode (data ).decode ('ascii' ))
56+ self ._stream .write ('\n ' )
57+
58+ def close (self ):
59+ self ._stream .close ()
3960
4061
4162tag_id_to_string_map = {
4263 asn1 .Numbers .Boolean : "BOOLEAN" ,
4364 asn1 .Numbers .Integer : "INTEGER" ,
4465 asn1 .Numbers .BitString : "BIT STRING" ,
45- asn1 .Numbers .OctetString : "OCTET STRING" ,
4666 asn1 .Numbers .Null : "NULL" ,
47- asn1 .Numbers .ObjectIdentifier : "OBJECT" ,
67+ asn1 .Numbers .OctetString : "OCTET STRING" ,
68+ asn1 .Numbers .ObjectIdentifier : "OBJECT IDENTIFIER" ,
69+ asn1 .Numbers .ObjectDescriptor : "OBJECT DESCRIPTOR" ,
4870 asn1 .Numbers .PrintableString : "PRINTABLESTRING" ,
4971 asn1 .Numbers .IA5String : "IA5STRING" ,
5072 asn1 .Numbers .UTCTime : "UTCTIME" ,
@@ -117,12 +139,11 @@ def object_identifier_to_string(identifier):
117139 return object_id_to_string_map [identifier ]
118140 return identifier
119141
120-
121142def value_to_string (tag_number , value ):
122143 if tag_number == asn1 .Numbers .ObjectIdentifier :
123144 return object_identifier_to_string (value )
124145 elif isinstance (value , bytes ):
125- return '0x' + str (binascii .hexlify (value ).upper ())
146+ return str (binascii .hexlify (value , ' ' , 1 ).upper (), encoding = 'ascii' )
126147 elif isinstance (value , str ):
127148 return value
128149 else :
@@ -133,6 +154,8 @@ def pretty_print(input_stream, output_stream, indent=0):
133154 """Pretty print ASN.1 data."""
134155 while not input_stream .eof ():
135156 tag = input_stream .peek ()
157+ if tag is None :
158+ return
136159 if tag .typ == asn1 .Types .Primitive :
137160 tag , value = input_stream .read ()
138161 output_stream .write (' ' * indent )
@@ -149,24 +172,24 @@ def pretty_print(input_stream, output_stream, indent=0):
149172
150173parser = optparse .OptionParser ()
151174parser .add_option ('-p' , '--pem' , dest = 'mode' , action = 'store_const' , const = 'pem' , help = 'PEM encoded input' )
152- parser .add_option ('-r' , '--raw' , dest = 'mode' , action = 'store_const' , const = 'raw' , help = 'raw input' )
153175parser .add_option ('-o' , '--output' , dest = 'output' , help = 'output to FILE instead' , metavar = 'FILE' )
154176parser .set_default ('mode' , 'pem' )
155177(opts , args ) = parser .parse_args ()
156178
179+ if len (args ) != 1 :
180+ parser .error ('Please provide an input file' )
181+ exit (1 )
182+
183+ decoder = asn1 .Decoder ()
184+
157185if opts .mode == 'pem' :
158- input_file = open (args [0 ], 'r' )
159- input_data = read_pem (input_file )
186+ decoder .start (PEMBinaryIO (open (args [0 ], 'r' )))
160187else :
161- input_file = open (args [0 ], 'rb' )
162- input_data = input_file .read ()
188+ decoder .start (typing .cast (io .RawIOBase , open (args [0 ], 'rb' )))
163189
164190if opts .output :
165191 output_file = open (opts .output , 'w' )
166192else :
167- output_file = sys .stdout
168-
169- decoder = asn1 .Decoder ()
170- decoder .start (input_data )
193+ output_file = typing .cast (io .TextIOWrapper , sys .stdout )
171194
172195pretty_print (decoder , output_file )
0 commit comments